"""
任务管理API接口
提供任务的增删改查、执行控制等API
"""
from flask import Blueprint, request, jsonify
from flask_login import login_required, current_user
from datetime import datetime
import json
import sys
import os
import requests

# 添加项目根目录到路径
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))

from services.shared.models import db
from services.shared.models.task import Task, TaskExecution
from services.shared.models.system import SystemLog
from services.shared.models.enums import TaskStatus, TaskType
from services.shared.models.user import User
from services.shared.models.script import ScriptFolder, UserScript
from services.shared.models.global_parameter import GlobalParameter
from services.shared.utils import (
    validate_cron_expression, get_next_run_time, parse_environment_variables,
    parse_dependencies, create_response, generate_execution_id
)
from services.shared.timezone_utils import now as timezone_now
from services.shared.service_client import get_scheduler_client, get_executor_client, get_service_registry
from services.shared.celery_app import celery_app
from services.shared.error_handler import log_api_exceptions
from services.shared.log_utils import (log_user_action, log_error, log_api_request, 
                                        log_task_operation, log_file_operation, log_system_event)
from services.shared.logger import get_logger

# 获取日志记录器
logger = get_logger('web.api')
from services.web.auth import admin_required
from functools import wraps
import time

# 创建蓝图
api_bp = Blueprint('api', __name__)

def log_api_call(f):
    """API调用日志装饰器"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        start_time = time.time()
        
        try:
            # 执行API函数
            result = f(*args, **kwargs)
            
            # 计算执行时间
            execution_time = (time.time() - start_time) * 1000  # 转换为毫秒
            
            # 获取状态码
            status_code = 200
            if hasattr(result, 'status_code'):
                status_code = result.status_code
            elif isinstance(result, tuple) and len(result) > 1:
                status_code = result[1]
            
            # 确定模块名
            endpoint = request.endpoint or request.path
            module_name = 'web_api'
            if '/admin/' in endpoint or 'admin' in f.__name__:
                module_name = 'admin_api'
            elif '/script' in endpoint or 'script' in f.__name__:
                module_name = 'script_api'
            elif '/dependencies' in endpoint or 'dependency' in f.__name__ or 'package' in f.__name__:
                module_name = 'dependency_api'
            elif '/tasks' in endpoint or 'task' in f.__name__:
                module_name = 'task_api'
            elif '/executions' in endpoint or 'execution' in f.__name__:
                module_name = 'execution_api'
            elif '/global-parameters' in endpoint or 'global_parameter' in f.__name__:
                module_name = 'parameter_api'
            elif '/recycle' in endpoint or 'recycle' in f.__name__:
                module_name = 'recycle_api'
            
            # 记录系统日志
            log_user_action(
                level='INFO' if status_code < 400 else 'ERROR',
                module=module_name,
                message=f'{request.method} {endpoint} - {status_code}',
                details=json.dumps({
                    'endpoint': endpoint,
                    'method': request.method,
                    'status_code': status_code,
                    'execution_time': execution_time,
                    'function_name': f.__name__,
                    'request_size': len(request.get_data()) if request.get_data() else 0,
                    'response_size': len(str(result)) if result else 0
                }, ensure_ascii=False),
                user_id=current_user.id if current_user.is_authenticated else None,
                action='api_call',
                resource_type='api_endpoint',
                resource_id=endpoint,
                execution_time=execution_time,
                status_code=status_code,
                tags=['api', request.method.lower(), module_name]
            )
            
            return result
            
        except Exception as e:
            # 计算执行时间
            execution_time = (time.time() - start_time) * 1000
            
            # 记录错误的API请求日志
            log_api_request(
                endpoint=request.endpoint or request.path,
                method=request.method,
                status_code=500,
                execution_time=execution_time,
                user_id=current_user.id if current_user.is_authenticated else None,
                request_size=len(request.get_data()) if request.get_data() else 0,
                error_message=str(e)
            )
            
            raise
    
    return decorated_function


@api_bp.route('/tasks', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_tasks():
    """获取任务列表"""
    logger.debug(f"开始处理获取任务列表请求，用户ID: {current_user.id}")
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 20, type=int)
    status = request.args.get('status')
    task_type = request.args.get('task_type')
    search = request.args.get('search')
    
    logger.debug(f"请求参数: page={page}, per_page={per_page}, status={status}, task_type={task_type}, search={search}")
    
    # 构建查询
    query = Task.query
    logger.debug("初始化任务查询")
    
    # 所有用户（包括管理员）只能看到自己的任务
    query = query.filter_by(user_id=current_user.id)
    logger.debug(f"添加用户过滤条件: user_id={current_user.id}")
    
    # 状态过滤
    if status:
        try:
            status_enum = TaskStatus(status)
            query = query.filter_by(status=status_enum)
            logger.debug(f"添加状态过滤条件: status={status_enum.value}")
        except ValueError:
            logger.debug(f"无效的状态值: {status}，忽略此过滤条件")
            pass
    
    # 类型过滤
    if task_type:
        try:
            type_enum = TaskType(task_type)
            query = query.filter_by(task_type=type_enum)
            logger.debug(f"添加类型过滤条件: task_type={type_enum.value}")
        except ValueError:
            logger.debug(f"无效的任务类型值: {task_type}，忽略此过滤条件")
            pass
    
    # 搜索过滤
    if search:
        query = query.filter(Task.name.contains(search))
        logger.debug(f"添加搜索过滤条件: name包含'{search}'")
    
    # 分页
    logger.debug(f"执行分页查询: page={page}, per_page={per_page}")
    pagination = query.order_by(Task.created_at.desc()).paginate(
        page=page, per_page=per_page, error_out=False
    )
    
    logger.debug(f"查询结果: 总记录数={pagination.total}, 总页数={pagination.pages}")
    tasks = [task.to_dict() for task in pagination.items]
    
    # 获取今日日期范围
    from datetime import datetime, time
    today = datetime.now().date()
    today_start = datetime.combine(today, time.min)
    today_end = datetime.combine(today, time.max)
    
    # 实时计算每个任务的下次执行时间和今日执行统计
    logger.debug(f"开始实时计算所有任务的下次执行时间和今日执行统计，任务数量: {len(tasks)}")
    for task_dict in tasks:
        task_item = next((t for t in pagination.items if t.id == task_dict['id']), None)
        
        # 计算下次执行时间
        if task_item and task_item.cron_expression:
            logger.debug(f"计算任务 ID: {task_item.id}, 名称: {task_item.name}, Cron表达式: {task_item.cron_expression}")
            try:
                next_run = get_next_run_time(task_item.cron_expression)
                if next_run:
                    logger.debug(f"任务 {task_item.id} 的下次执行时间: {next_run}")
                    task_dict['next_run_at'] = next_run.isoformat()
                else:
                    logger.warning(f"任务 {task_item.id} 的下次执行时间计算失败，Cron表达式: {task_item.cron_expression}")
                    task_dict['next_run_at'] = None
            except Exception as e:
                logger.error(f"计算任务 {task_item.id} 的下次执行时间时出错: {str(e)}")
                task_dict['next_run_at'] = None
        else:
            if not task_item:
                logger.warning(f"未找到任务项，ID: {task_dict['id']}")
            elif not task_item.cron_expression:
                logger.debug(f"任务 {task_item.id} 没有Cron表达式，跳过计算下次执行时间")
                task_dict['next_run_at'] = None
        
        # 计算今日执行统计
        if task_item:
            # 查询今日执行总数
            today_executions = TaskExecution.query.filter(
                TaskExecution.task_id == task_item.id,
                TaskExecution.started_at >= today_start,
                TaskExecution.started_at <= today_end
            ).count()
            
            # 查询今日成功执行数
            today_success = TaskExecution.query.filter(
                TaskExecution.task_id == task_item.id,
                TaskExecution.started_at >= today_start,
                TaskExecution.started_at <= today_end,
                TaskExecution.status == TaskStatus.SUCCESS
            ).count()
            
            task_dict['today_executions'] = today_executions
            task_dict['today_success'] = today_success
            logger.debug(f"任务 {task_item.id} 今日执行统计: 总数={today_executions}, 成功={today_success}")
        else:
            task_dict['today_executions'] = 0
            task_dict['today_success'] = 0
    
    logger.debug(f"实时计算所有任务的下次执行时间和今日执行统计完成")
    
    logger.debug(f"返回任务数量: {len(tasks)}")
    
    response_data = {
        'tasks': tasks,
        'pagination': {
            'page': page,
            'per_page': per_page,
            'total': pagination.total,
            'pages': pagination.pages,
            'has_prev': pagination.has_prev,
            'has_next': pagination.has_next
        }
    }
    logger.debug("任务列表查询完成，准备返回结果")
    return jsonify(create_response(True, '获取成功', response_data))


@api_bp.route('/tasks', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def create_task():
    """创建新任务"""
    logger.debug(f"开始处理创建任务请求，用户ID: {current_user.id}")
    
    # 检查是否为文件上传请求
    if request.content_type and 'multipart/form-data' in request.content_type:
        # 处理文件上传
        data = request.form.to_dict()
        script_file = request.files.get('script_file')
        logger.debug(f"接收到文件上传请求，数据: {data}")
    else:
        # 处理JSON请求
        data = request.get_json()
        script_file = None
        logger.debug(f"接收到JSON请求，数据: {data}")
    
    # 验证必需字段
    required_fields = ['name', 'task_type', 'cron_expression']
    logger.debug(f"开始验证必需字段: {required_fields}")
    for field in required_fields:
        if not data.get(field):
            logger.debug(f"缺少必需字段: {field}")
            return jsonify(create_response(False, f'缺少必需字段: {field}')), 400
    
    # 验证脚本内容或文件
    user_script_id = data.get('user_script_id')
    command_content = data.get('command_content')
    task_type = data.get('task_type')
    
    logger.debug(f"任务类型: {task_type}, 用户脚本ID: {user_script_id}, 命令内容: {command_content}")
    
    script_file_path = None
    
    if task_type == 'python_script':
        if script_file:
            # 处理文件上传
            import os
            import uuid
            from werkzeug.utils import secure_filename
            
            # 创建用户脚本目录
            user_scripts_dir = os.path.join('user_scripts', str(current_user.id))
            os.makedirs(user_scripts_dir, exist_ok=True)
            
            # 生成唯一文件名
            filename = secure_filename(script_file.filename)
            if not filename.endswith('.py'):
                filename += '.py'
            unique_filename = f"{uuid.uuid4().hex}_{filename}"
            script_file_path = os.path.join(user_scripts_dir, unique_filename)
            
            # 保存文件
            script_file.save(script_file_path)
            logger.debug(f"脚本文件已保存到: {script_file_path}")
        elif user_script_id:
            # 使用现有用户脚本
            logger.debug(f"使用用户脚本ID: {user_script_id}")
        else:
            logger.debug("Python脚本任务需要提供脚本文件或选择用户脚本")
            return jsonify(create_response(False, 'Python脚本任务需要提供脚本文件或选择用户脚本')), 400

    
    # 验证用户脚本权限
    if user_script_id:
        logger.debug(f"验证用户脚本权限，脚本ID: {user_script_id}")
        user_script = UserScript.query.filter_by(
            id=user_script_id, 
            user_id=current_user.id,
            is_active=True
        ).first()
        if not user_script:
            logger.debug(f"用户脚本验证失败: 脚本ID {user_script_id} 不存在或无权访问")
            return jsonify(create_response(False, '选择的脚本不存在或无权访问')), 400
        logger.debug(f"用户脚本验证成功: {user_script.name}")
    
    # 验证Cron表达式
    logger.debug(f"验证Cron表达式: {data['cron_expression']}")
    if not validate_cron_expression(data['cron_expression']):
        logger.debug(f"Cron表达式格式错误: {data['cron_expression']}")
        return jsonify(create_response(False, 'Cron表达式格式错误')), 400
    logger.debug("Cron表达式验证通过")
    
    # 验证任务类型
    try:
        task_type = TaskType(data['task_type'])
        logger.debug(f"任务类型验证通过: {task_type.value}")
    except ValueError:
        logger.debug(f"无效的任务类型: {data['task_type']}")
        return jsonify(create_response(False, '无效的任务类型')), 400
    
    # 创建任务
    logger.debug("开始创建任务对象")
    task = Task(
        name=data['name'],
        description=data.get('description', ''),
        task_type=TaskType(task_type),
        cron_expression=data['cron_expression'],
        script_file_path=script_file_path,
        working_directory=data.get('working_directory'),
        environment_variables=json.dumps(data.get('environment_variables', {})),
        timeout=data.get('timeout', 0),  # 修改默认值为0（无超时限制）
        retry_attempts=data.get('retry_attempts', 0),
        user_id=current_user.id,
        user_script_id=user_script_id,
        is_enabled=data.get('is_enabled', True),
        notify_on_failure=data.get('notify_on_failure', False),
        notification_config_id=data.get('notification_config_id')
    )
    logger.debug(f"任务对象创建完成: {task.name}")
    
    # 不再计算并存储下次执行时间到数据库，改为通过API实时计算
    # task.next_run_at = get_next_run_time(task.cron_expression)
    logger.debug(f"不再计算并存储下次执行时间到数据库，改为通过API实时计算")
    
    try:
        logger.debug("开始将任务添加到数据库")
        db.session.add(task)
        db.session.commit()
        logger.debug(f"任务成功添加到数据库，ID: {task.id}")
        
        # 记录任务操作日志
        log_task_operation(
            action='create',
            task_id=task.id,
            task_name=task.name,
            user_id=current_user.id,
            execution_time=None,
            status_code=200,
            error_message=None,
            details=f'创建任务: {task.name}'
        )
        logger.debug(f"已记录任务操作日志: 用户 {current_user.username} 创建了任务: {task.name}")
        
        # 如果任务启用，添加到调度器
        if task.is_enabled:
            logger.debug("任务已启用，准备添加到调度器")
            scheduler_client = get_scheduler_client()
            logger.debug("已获取调度器客户端，准备添加任务")
            scheduler_result = scheduler_client.add_task(task.to_dict())
            logger.debug(f"调度器添加任务结果: {scheduler_result}")
            if not scheduler_result.get('success'):
                logger.error(f"添加任务到调度器失败: {scheduler_result.get('error')}")
                log_error(
                    module='api',
                    message="添加任务到调度器失败",
                    details=json.dumps({
                        'task_id': task.id,
                        'task_name': task.name,
                        'error': scheduler_result.get('error')
                    }, ensure_ascii=False)
                )
        else:
            logger.debug("任务未启用，跳过添加到调度器")
        
        logger.debug(f"任务创建成功，准备返回结果，任务ID: {task.id}")
        return jsonify(create_response(True, '任务创建成功', task.to_dict())), 201
    
    except Exception as e:
        logger.debug(f"创建任务时发生异常: {str(e)}")
        import traceback
        logger.debug(f"异常详情: {traceback.format_exc()}")
        db.session.rollback()
        return jsonify(create_response(False, f'创建任务失败: {str(e)}')), 500


@api_bp.route('/tasks/<int:task_id>', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_task(task_id):
    """获取单个任务详情"""
    task = Task.query.get_or_404(task_id)
    
    # 权限检查 - 所有用户只能查看自己的任务
    if task.user_id != current_user.id:
        return jsonify(create_response(False, '权限不足')), 403
    
    # 获取任务字典
    task_dict = task.to_dict()
    
    # 实时计算下次执行时间
    if task.cron_expression:
        logger.debug(f"计算任务 ID: {task.id}, 名称: {task.name}, Cron表达式: {task.cron_expression}")
        next_run = get_next_run_time(task.cron_expression)
        if next_run:
            logger.debug(f"任务 {task.id} 的下次执行时间: {next_run}")
            task_dict['next_run_at'] = next_run.isoformat()
        else:
            logger.warning(f"任务 {task.id} 的下次执行时间计算失败，Cron表达式: {task.cron_expression}")
            task_dict['next_run_at'] = None
    else:
        logger.debug(f"任务 {task.id} 没有Cron表达式，跳过计算下次执行时间")
    
    return jsonify(create_response(True, '获取成功', task_dict))


@api_bp.route('/tasks/<int:task_id>', methods=['PUT'])
@login_required
@log_api_exceptions
@log_api_call
def update_task(task_id):
    """更新任务"""
    task = Task.query.get_or_404(task_id)
    
    # 权限检查 - 所有用户只能修改自己的任务
    if task.user_id != current_user.id:
        return jsonify(create_response(False, '权限不足')), 403
    
    data = request.get_json()
    
    # 验证Cron表达式（如果提供）
    if 'cron_expression' in data and not validate_cron_expression(data['cron_expression']):
        return jsonify(create_response(False, 'Cron表达式格式错误')), 400
    
    # 验证任务类型（如果提供）
    if 'task_type' in data:
        try:
            TaskType(data['task_type'])
        except ValueError:
            return jsonify(create_response(False, '无效的任务类型')), 400
    
    # 更新字段
    updateable_fields = [
        'name', 'description', 'task_type', 'cron_expression', 'script_file_path', 'command_content',
        'timeout', 'retry_attempts', 'is_enabled', 'user_script_id', 'notify_on_failure', 'notification_config_id'
    ]
    
    # 处理用户脚本更新
    if 'user_script_id' in data:
        user_script_id = data['user_script_id']
        if user_script_id:
            # 验证用户脚本权限
            user_script = UserScript.query.filter_by(
                id=user_script_id, 
                user_id=current_user.id,
                is_active=True
            ).first()
            if not user_script:
                return jsonify(create_response(False, '选择的脚本不存在或无权访问')), 400
    
    # 处理环境变量
    if 'environment_variables' in data:
        task.environment_variables = json.dumps(data['environment_variables'])
    
    for field in updateable_fields:
        if field in data and field != 'environment_variables':  # 排除环境变量字段，避免覆盖
            if field == 'task_type':
                setattr(task, field, TaskType(data[field]))
            else:
                setattr(task, field, data[field])
    
    # 不再更新数据库中的 next_run_at 字段，该字段将通过 API 实时计算
    # if 'cron_expression' in data:
    #     task.next_run_at = get_next_run_time(task.cron_expression)
    
    task.updated_at = datetime.utcnow()
    
    try:
        db.session.commit()
        
        # 记录任务操作日志
        log_task_operation(
            action='update',
            task_id=task.id,
            task_name=task.name,
            user_id=current_user.id,
            execution_time=None,
            status_code=200,
            error_message=None,
            details=f'更新任务: {task.name}'
        )
        
        # 更新调度器中的任务
        scheduler_client = get_scheduler_client()
        if task.is_enabled:
            scheduler_result = scheduler_client.update_task(task_id, task.to_dict())
        else:
            scheduler_result = scheduler_client.disable_task(task_id)
        
        if not scheduler_result.get('success'):
            logger.error(f"更新调度器中的任务失败: {scheduler_result.get('error')}")
        
        return jsonify(create_response(True, '任务更新成功', task.to_dict()))
    
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'更新任务失败: {str(e)}')), 500


@api_bp.route('/tasks/<int:task_id>', methods=['DELETE'])
@login_required
@log_api_exceptions
@log_api_call
def delete_task(task_id):
    """删除任务"""
    logger.debug(f"开始处理删除任务请求，任务ID: {task_id}，用户ID: {current_user.id}")
    task = Task.query.get_or_404(task_id)
    logger.debug(f"找到任务: {task.name}，所有者ID: {task.user_id}")
    
    # 权限检查 - 所有用户只能删除自己的任务
    if task.user_id != current_user.id:
        logger.debug(f"权限检查失败: 用户ID {current_user.id} 尝试删除用户ID {task.user_id} 的任务")
        return jsonify(create_response(False, '权限不足')), 403
    
    task_name = task.name
    logger.debug(f"准备删除任务: {task_name}")
    
    try:
        logger.debug(f"从数据库中删除任务: {task_id}")
        db.session.delete(task)
        db.session.commit()
        logger.debug(f"任务已从数据库中删除: {task_id}")
        
        # 从调度器中移除任务
        logger.debug(f"准备从调度器中移除任务: {task_id}")
        scheduler_client = get_scheduler_client()
        scheduler_result = scheduler_client.remove_task(task_id)
        logger.debug(f"调度器移除任务结果: {scheduler_result}")
        if not scheduler_result.get('success'):
            logger.error(f"从调度器移除任务失败: {scheduler_result.get('error')}")
        
        # 记录任务操作日志
        logger.debug(f"记录任务操作日志: 用户 {current_user.username} 删除了任务: {task_name}")
        log_task_operation(
            action='delete',
            task_id=task_id,
            task_name=task_name,
            user_id=current_user.id,
            execution_time=None,
            status_code=200,
            error_message=None,
            details=f'删除任务: {task_name}'
        )
        
        logger.debug(f"任务删除成功，准备返回结果: {task_id}")
        return jsonify(create_response(True, '任务删除成功'))
    
    except Exception as e:
        logger.debug(f"删除任务时发生异常: {str(e)}")
        import traceback
        logger.debug(f"异常详情: {traceback.format_exc()}")
        db.session.rollback()
        return jsonify(create_response(False, f'删除任务失败: {str(e)}')), 500


@api_bp.route('/tasks/<int:task_id>/toggle', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def toggle_task(task_id):
    """启用/禁用任务"""
    logger.debug(f"开始处理任务状态切换请求，任务ID: {task_id}，用户ID: {current_user.id}")
    task = Task.query.get_or_404(task_id)
    logger.debug(f"找到任务: {task.name}，当前状态: {'启用' if task.is_enabled else '禁用'}")
    
    # 权限检查 - 所有用户只能操作自己的任务
    if task.user_id != current_user.id:
        logger.debug(f"权限检查失败: 用户ID {current_user.id} 尝试操作用户ID {task.user_id} 的任务")
        return jsonify(create_response(False, '权限不足')), 403
    
    task.is_enabled = not task.is_enabled
    task.updated_at = datetime.utcnow()
    logger.debug(f"切换任务状态为: {'启用' if task.is_enabled else '禁用'}")
    
    try:
        logger.debug("提交数据库更改")
        db.session.commit()
        
        status = '启用' if task.is_enabled else '禁用'
        logger.debug(f"任务状态已更新为: {status}")
        
        # 更新调度器中的任务状态
        logger.debug(f"准备更新调度器中的任务状态: {task_id}, 新状态: {status}")
        scheduler_client = get_scheduler_client()
        if task.is_enabled:
            logger.debug(f"调用调度器启用任务: {task_id}")
            scheduler_result = scheduler_client.enable_task(task_id)
        else:
            logger.debug(f"调用调度器禁用任务: {task_id}")
            scheduler_result = scheduler_client.disable_task(task_id)
        
        logger.debug(f"调度器更新任务状态结果: {scheduler_result}")
        if not scheduler_result.get('success'):
            logger.error(f"更新调度器任务状态失败: {scheduler_result.get('error')}")
        
        # 记录任务操作日志
        logger.debug(f"记录任务操作日志: 用户 {current_user.username} {status}了任务: {task.name}")
        log_task_operation(
            action='toggle',
            task_id=task.id,
            task_name=task.name,
            user_id=current_user.id,
            execution_time=None,
            status_code=200,
            error_message=None,
            details=f'{status}任务: {task.name}'
        )
        logger.debug("任务操作日志已记录")
        
        logger.debug(f"任务状态切换成功，准备返回结果: {task_id}")
        return jsonify(create_response(True, f'任务{status}成功', task.to_dict()))
    
    except Exception as e:
        logger.debug(f"切换任务状态时发生异常: {str(e)}")
        import traceback
        logger.debug(f"异常详情: {traceback.format_exc()}")
        db.session.rollback()
        return jsonify(create_response(False, f'操作任务失败: {str(e)}')), 500


@api_bp.route('/tasks/<int:task_id>/execute', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def execute_task(task_id):
    """手动执行任务"""
    logger.debug(f"开始处理手动执行任务请求，任务ID: {task_id}，用户ID: {current_user.id}")
    task = Task.query.get_or_404(task_id)
    logger.debug(f"找到任务: {task.name}，任务类型: {task.task_type.value}")
    
    # 权限检查 - 所有用户只能执行自己的任务
    if task.user_id != current_user.id:
        logger.debug(f"权限检查失败: 用户ID {current_user.id} 尝试执行用户ID {task.user_id} 的任务")
        return jsonify(create_response(False, '权限不足')), 403
    
    # 检查任务是否启用
    if not task.is_enabled:
        logger.debug(f"任务未启用，无法执行: 任务ID {task_id}, is_enabled: {task.is_enabled}")
        return jsonify(create_response(False, '任务未启用，无法执行')), 400
    
    # 创建执行记录
    execution_id = generate_execution_id()
    logger.debug(f"生成执行ID: {execution_id}")
    execution = TaskExecution(
        task_id=task.id,
        execution_id=execution_id,
        status=TaskStatus.PENDING
        # 不在这里设置started_at，而是在任务真正开始执行时设置
    )
    logger.debug(f"创建执行记录: task_id={task.id}, execution_id={execution_id}, status=PENDING, started_at={execution.started_at}")
    
    try:
        logger.debug("将执行记录添加到数据库")
        db.session.add(execution)
        db.session.commit()
        logger.debug(f"执行记录已添加到数据库，ID: {execution.id}")
        
        # 记录任务操作日志
        logger.debug(f"记录任务操作日志: 用户 {current_user.username} 手动执行任务: {task.name}")
        log_task_operation(
            action='execute',
            task_id=task.id,
            task_name=task.name,
            user_id=current_user.id,
            execution_time=None,
            status_code=200,
            error_message=None,
            details=f'手动执行任务: {task.name}'
        )
        
        # 发送任务到执行器服务
        logger.debug("获取执行器客户端")
        executor_client = get_executor_client()
        
        # 获取实际执行的脚本内容
        logger.debug("开始获取实际执行的脚本内容")
        actual_script = None
        if task.user_script_id:
            logger.debug(f"任务使用用户脚本，脚本ID: {task.user_script_id}")
            # 使用用户脚本
            user_script = UserScript.query.filter_by(
                id=task.user_script_id,
                user_id=task.user_id,
                is_active=True
            ).first()
            if user_script:
                logger.debug(f"找到用户脚本: {user_script.name}")
                # 从文件系统读取脚本内容
                try:
                    logger.debug(f"尝试从文件系统读取脚本内容: {user_script.name}")
                    from services.shared.file_utils import read_user_script_content
                    username = task.creator.username if task.creator else current_user.username
                    actual_script = read_user_script_content(username, user_script.name)
                    if actual_script is None:
                        logger.debug(f"脚本内容读取失败: {user_script.name}")
                        actual_script = "# 脚本内容读取失败"
                    else:
                        logger.debug(f"脚本内容读取成功，长度: {len(actual_script)}字节")
                except Exception as e:
                    logger.debug(f"读取脚本内容时发生错误: {str(e)}")
                    import traceback
                    logger.debug(f"异常详情: {traceback.format_exc()}")
                    actual_script = f"# 读取脚本内容时发生错误: {str(e)}"
            else:
                logger.debug(f"用户脚本不存在或已禁用: {task.user_script_id}")
                execution.status = TaskStatus.FAILED
                execution.error_message = '关联的用户脚本不存在或已禁用'
                db.session.commit()
                logger.debug("更新执行记录状态为FAILED，准备返回错误响应")
                return jsonify(create_response(False, '执行任务失败: 关联的用户脚本不存在或已禁用')), 500
        else:
            logger.debug("任务没有关联用户脚本")
            execution.status = TaskStatus.FAILED
            execution.error_message = '任务没有关联的用户脚本'
            db.session.commit()
            logger.debug("更新执行记录状态为FAILED，准备返回错误响应")
            return jsonify(create_response(False, '执行任务失败: 任务没有关联的用户脚本')), 500
        
        logger.debug("构建任务数据")
        task_data = {
            'task_id': task.id,
            'execution_id': execution.execution_id,
            'task_type': task.task_type.value,
            'script_content': actual_script,
            'working_directory': task.working_directory,
            'environment_variables': task.environment_variables,

            'timeout': task.timeout,
            'retry_attempts': task.retry_attempts
        }
        logger.debug(f"任务数据构建完成: task_id={task.id}, execution_id={execution.execution_id}, task_type={task.task_type.value}")
        
        # 如果使用用户脚本，添加用户脚本信息
        if task.user_script_id:
            logger.debug("添加用户脚本信息到任务数据")
            user_script = UserScript.query.filter_by(
                id=task.user_script_id,
                user_id=task.user_id,
                is_active=True
            ).first()
            if user_script:
                username = task.creator.username if task.creator else current_user.username
                logger.debug(f"添加用户脚本信息: username={username}, script_name={user_script.name}")
                task_data['user_script_info'] = {
                    'username': username,
                    'script_name': user_script.name
                }
        
        # 使用ExecutorClient执行任务
        try:
            logger.debug("导入ExecutorClient")
            from services.shared.service_client import ExecutorClient
            
            logger.debug("创建执行器客户端，使用Celery模式")
            executor_client = ExecutorClient(use_celery=True)
            
            logger.debug(f"准备调用执行器客户端执行任务: task_id={task.id}, execution_id={execution.execution_id}")
            execute_result = executor_client.execute_task(task_data)
            logger.debug(f"执行器客户端返回结果: {execute_result}")
            
            if not execute_result.get('success'):
                error_msg = execute_result.get('error', '任务执行失败')
                logger.debug(f"任务执行失败: {error_msg}")
                execution.status = TaskStatus.FAILED
                execution.error_message = error_msg
                db.session.commit()
                logger.debug("更新执行记录状态为FAILED，准备返回错误响应")
                return jsonify(create_response(False, f'任务执行失败: {error_msg}')), 500
            
            # 更新执行状态 - 对于Celery异步执行，状态会保持为RUNNING
            # 任务完成后会由Celery worker更新状态
            celery_task_id = execute_result.get('celery_task_id')
            if celery_task_id:
                logger.debug(f"更新执行记录的Celery任务ID: {celery_task_id}")
                execution.celery_task_id = celery_task_id
                execution.status = TaskStatus.RUNNING
                db.session.commit()
                logger.debug("更新执行记录状态为RUNNING")
            
            logger.debug("任务已成功提交执行，准备返回成功响应")
            return jsonify(create_response(True, '任务已提交执行', execution.to_dict()))
        except Exception as e:
            logger.debug(f"执行任务时发生异常: {str(e)}")
            import traceback
            logger.debug(f"异常详情: {traceback.format_exc()}")
            
            execution.status = TaskStatus.FAILED
            execution.error_message = f'执行任务时发生异常: {str(e)}'
            db.session.commit()
            logger.debug("更新执行记录状态为FAILED，准备返回错误响应")
            return jsonify(create_response(False, f'执行任务失败: {str(e)}')), 500
    
    except Exception as e:
        logger.debug(f"处理执行任务请求时发生异常: {str(e)}")
        import traceback
        logger.debug(f"异常详情: {traceback.format_exc()}")
        db.session.rollback()
        return jsonify(create_response(False, f'执行任务失败: {str(e)}')), 500


@api_bp.route('/tasks/<int:task_id>/executions', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_task_executions(task_id):
    """获取任务执行记录"""
    logger.debug(f"开始处理获取任务执行记录请求，任务ID: {task_id}，用户ID: {current_user.id}")
    try:
        task = Task.query.get_or_404(task_id)
        logger.debug(f"找到任务: {task.name}，任务类型: {task.task_type.value}")
        
        # 权限检查 - 所有用户只能查看自己任务的执行记录
        if task.user_id != current_user.id:
            logger.debug(f"权限检查失败: 用户ID {current_user.id} 尝试查看用户ID {task.user_id} 的任务执行记录")
            return jsonify(create_response(False, '权限不足')), 403
        
        logger.debug("解析分页参数")
        page = request.args.get('page', 1, type=int)
        per_page = request.args.get('per_page', 20, type=int)
        logger.debug(f"分页参数: page={page}, per_page={per_page}")
        
        logger.debug(f"查询任务ID {task_id} 的执行记录")
        pagination = TaskExecution.query.filter_by(task_id=task_id)\
            .order_by(TaskExecution.started_at.desc())\
            .paginate(page=page, per_page=per_page, error_out=False)
        
        logger.debug(f"查询到 {len(pagination.items)} 条执行记录")
        executions = [execution.to_dict() for execution in pagination.items]
        
        # 计算总的执行统计信息
        logger.debug("计算执行统计信息")
        total_executions = TaskExecution.query.filter_by(task_id=task_id).count()
        success_executions = TaskExecution.query.filter_by(task_id=task_id, status=TaskStatus.SUCCESS).count()
        failed_executions = TaskExecution.query.filter_by(task_id=task_id, status=TaskStatus.FAILED).count()
        running_executions = TaskExecution.query.filter_by(task_id=task_id, status=TaskStatus.RUNNING).count()
        pending_executions = TaskExecution.query.filter_by(task_id=task_id, status=TaskStatus.PENDING).count()
        
        success_rate = round(success_executions / total_executions * 100, 2) if total_executions > 0 else 0
        logger.debug(f"执行统计信息: 总数={total_executions}, 成功={success_executions}, 失败={failed_executions}, 运行中={running_executions}, 等待中={pending_executions}, 成功率={success_rate}%")
        
        logger.debug("准备返回执行记录数据")
        return jsonify(create_response(True, '获取成功', {
            'executions': executions,
            'pagination': {
                'page': page,
                'per_page': per_page,
                'total': pagination.total,
                'pages': pagination.pages,
                'has_prev': pagination.has_prev,
                'has_next': pagination.has_next
            },
            'stats': {
                'total_executions': total_executions,
                'success_executions': success_executions,
                'failed_executions': failed_executions,
                'running_executions': running_executions,
                'pending_executions': pending_executions,
                'success_rate': success_rate
            }
        }))
    except Exception as e:
        logger.debug(f"获取任务执行记录时发生异常: {str(e)}")
        import traceback
        logger.debug(f"异常详情: {traceback.format_exc()}")
        return jsonify(create_response(False, f'获取任务执行记录失败: {str(e)}')), 500


@api_bp.route('/executions/<int:execution_id>', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_execution_detail(execution_id):
    """获取单个执行记录详情"""
    logger.debug(f"开始处理获取执行记录详情请求，执行ID: {execution_id}，用户ID: {current_user.id}")
    try:
        logger.debug(f"查询执行记录: {execution_id}")
        execution = TaskExecution.query.get_or_404(execution_id)
        logger.debug(f"找到执行记录: {execution.execution_id}, 任务ID: {execution.task_id}, 状态: {execution.status.value}")
        
        # 权限检查
        task_user_id = execution.task.user_id if execution.task else None
        logger.debug(f"执行权限检查: 当前用户ID={current_user.id}, 任务所有者ID={task_user_id}, 是否管理员={current_user.is_admin}")
        if not current_user.is_admin and task_user_id != current_user.id:
            logger.debug(f"权限检查失败: 用户ID {current_user.id} 尝试查看用户ID {task_user_id} 的执行记录")
            return jsonify(create_response(False, '权限不足')), 403
        
        # 获取任务名称
        logger.debug("构建执行记录详情数据")
        execution_dict = execution.to_dict()
        task_name = execution.task.name if execution.task else '未知任务'
        logger.debug(f"关联任务名称: {task_name}")
        execution_dict['task_name'] = task_name
        
        logger.debug("准备返回执行记录详情数据")
        return jsonify(create_response(True, '获取成功', execution_dict))
    except Exception as e:
        logger.debug(f"获取执行记录详情时发生异常: {str(e)}")
        import traceback
        logger.debug(f"异常详情: {traceback.format_exc()}")
        return jsonify(create_response(False, f'获取执行记录详情失败: {str(e)}')), 500


@api_bp.route('/executions', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_executions():
    """获取执行记录列表"""
    logger.debug(f"开始处理获取执行记录列表请求，用户ID: {current_user.id}")
    try:
        logger.debug("解析请求参数")
        page = request.args.get('page', 1, type=int)
        per_page = request.args.get('per_page', 20, type=int)
        status = request.args.get('status')
        task_id = request.args.get('task_id', type=int)
        date = request.args.get('date')
        logger.debug(f"请求参数: page={page}, per_page={per_page}, status={status}, task_id={task_id}, date={date}")
        
        # 构建基础查询 - 所有用户只能查看自己任务的执行记录
        logger.debug(f"构建基础查询，过滤用户ID: {current_user.id}")
        query = TaskExecution.query.join(Task).filter(Task.user_id == current_user.id)
        
        # 状态过滤
        if status:
            logger.debug(f"应用状态过滤: {status}")
            try:
                status_enum = TaskStatus(status)
                query = query.filter(TaskExecution.status == status_enum)
                logger.debug(f"状态过滤应用成功: {status_enum.value}")
            except ValueError:
                logger.debug(f"无效的状态值: {status}，忽略此过滤条件")
                pass
        
        # 任务过滤
        if task_id:
            logger.debug(f"应用任务ID过滤: {task_id}")
            query = query.filter(TaskExecution.task_id == task_id)
        
        # 日期过滤
        if date:
            logger.debug(f"应用日期过滤: {date}")
            try:
                from datetime import datetime as dt
                date_obj = dt.strptime(date, '%Y-%m-%d')
                next_date = date_obj.replace(hour=23, minute=59, second=59)
                logger.debug(f"日期范围: {date_obj} 到 {next_date}")
                query = query.filter(
                    TaskExecution.started_at >= date_obj,
                    TaskExecution.started_at <= next_date
                )
            except ValueError:
                logger.debug(f"无效的日期格式: {date}，忽略此过滤条件")
                pass
        
        # 分页查询
        logger.debug(f"执行分页查询: page={page}, per_page={per_page}")
        pagination = query.order_by(TaskExecution.started_at.desc()).paginate(
            page=page, per_page=per_page, error_out=False
    )
    
        # 获取执行记录并加上任务名称
        logger.debug(f"查询到 {len(pagination.items)} 条执行记录，开始处理结果")
        executions = []
        for execution in pagination.items:
            execution_dict = execution.to_dict()
            # 添加任务名称
            if execution.task:
                task_name = execution.task.name
                logger.debug(f"执行记录 {execution.execution_id} 关联任务名称: {task_name}")
            else:
                task_name = '未知任务'
                logger.debug(f"执行记录 {execution.execution_id} 无关联任务")
            execution_dict['task_name'] = task_name
            executions.append(execution_dict)
        
        logger.debug("准备返回执行记录列表数据")
        return jsonify(create_response(True, '获取成功', {
            'executions': executions,
            'pagination': {
                'page': page,
            'per_page': per_page,
            'total': pagination.total,
            'pages': pagination.pages,
            'has_prev': pagination.has_prev,
            'has_next': pagination.has_next
        }
    }))
    except Exception as e:
        logger.debug(f"获取执行记录列表时发生异常: {str(e)}")
        import traceback
        logger.debug(f"异常详情: {traceback.format_exc()}")
        return jsonify(create_response(False, f'获取执行记录列表失败: {str(e)}')), 500


@api_bp.route('/executions/<execution_id>', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_execution_detail_by_id(execution_id):
    """根据execution_id获取执行记录详情"""
    logger.debug(f"开始处理根据execution_id获取执行记录详情请求，execution_id: {execution_id}，用户ID: {current_user.id}")
    try:
        logger.debug(f"查询执行记录: execution_id={execution_id}")
        execution = TaskExecution.query.filter_by(execution_id=execution_id).first_or_404()
        logger.debug(f"找到执行记录: ID={execution.id}, 任务ID={execution.task_id}, 状态={execution.status.value}")
        
        # 权限检查
        task_user_id = execution.task.user_id if execution.task else None
        logger.debug(f"执行权限检查: 当前用户ID={current_user.id}, 任务所有者ID={task_user_id}, 是否管理员={current_user.is_admin}")
        if not current_user.is_admin and execution.task and task_user_id != current_user.id:
            logger.debug(f"权限检查失败: 用户ID {current_user.id} 尝试查看用户ID {task_user_id} 的执行记录")
            return jsonify(create_response(False, '权限不足')), 403
        
        # 获取任务名称
        logger.debug("构建执行记录详情数据")
        execution_dict = execution.to_dict()
        task_name = execution.task.name if execution.task else '未知任务'
        logger.debug(f"关联任务名称: {task_name}")
        execution_dict['task_name'] = task_name
        
        logger.debug("准备返回执行记录详情数据")
        return jsonify(create_response(True, '获取成功', execution_dict))
    except Exception as e:
        logger.debug(f"获取执行记录详情时发生异常: {str(e)}")
        import traceback
        logger.debug(f"异常详情: {traceback.format_exc()}")
        return jsonify(create_response(False, f'获取执行记录详情失败: {str(e)}')), 500


@api_bp.route('/tasks/<int:task_id>/executions/clear', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def clear_task_executions(task_id):
    """清除任务执行记录"""
    task = Task.query.get_or_404(task_id)
    
    # 权限检查 - 所有用户只能清除自己任务的执行记录
    if task.user_id != current_user.id:
        return jsonify(create_response(False, '权限不足')), 403
    
    try:
        # 删除该任务的所有执行记录
        executions = TaskExecution.query.filter_by(task_id=task_id).all()
        execution_count = len(executions)
        
        for execution in executions:
            db.session.delete(execution)
        
        db.session.commit()
        
        # 记录系统日志
        log_user_action('INFO', 'task_api', f'用户 {current_user.username} 清除了任务 {task.name} 的 {execution_count} 条执行记录')
        
        return jsonify(create_response(True, f'成功清除 {execution_count} 条执行记录'))
    
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'清除执行记录失败: {str(e)}')), 500


@api_bp.route('/stats/overview', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_overview_stats():
    """获取概览统计"""
    from sqlalchemy import func
    
    # 构建基础查询
    if current_user.is_admin:
        task_query = Task.query
        execution_query = TaskExecution.query
    else:
        task_query = Task.query.filter_by(user_id=current_user.id)
        execution_query = TaskExecution.query.join(Task).filter(Task.user_id == current_user.id)
    
    # 任务统计
    total_tasks = task_query.count()
    active_tasks = task_query.filter_by(is_enabled=True).count()
    
    # 执行统计
    total_executions = execution_query.count()
    success_executions = execution_query.filter_by(status=TaskStatus.SUCCESS).count()
    failed_executions = execution_query.filter_by(status=TaskStatus.FAILED).count()
    
    # 最近执行
    recent_executions = execution_query.order_by(TaskExecution.started_at.desc()).limit(10).all()
    
    return jsonify(create_response(True, '获取成功', {
        'total_tasks': total_tasks,
        'active_tasks': active_tasks,
        'total_executions': total_executions,
        'success_executions': success_executions,
        'failed_executions': failed_executions,
        'success_rate': round(success_executions / total_executions * 100, 2) if total_executions > 0 else 0,
        'recent_executions': [execution.to_dict() for execution in recent_executions]
    }))


# 管理员专用接口
@api_bp.route('/admin/users', methods=['GET'])
@login_required
@admin_required
@log_api_call
def get_users():
    """获取用户列表（管理员专用）"""
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 20, type=int)
    
    pagination = User.query.order_by(User.id.asc())\
        .paginate(page=page, per_page=per_page, error_out=False)
    
    users = [user.to_dict() for user in pagination.items]
    
    return jsonify(create_response(True, '获取成功', {
        'users': users,
        'pagination': {
            'page': page,
            'per_page': per_page,
            'total': pagination.total,
            'pages': pagination.pages,
            'has_prev': pagination.has_prev,
            'has_next': pagination.has_next
        }
    }))


@api_bp.route('/admin/users', methods=['POST'])
@login_required
@admin_required
@log_api_call
def create_user():
    """创建新用户（管理员专用）"""
    data = request.get_json()
    
    # 验证必需字段
    required_fields = ['username', 'email', 'password']
    for field in required_fields:
        if not data.get(field):
            return jsonify(create_response(False, f'缺少必需字段: {field}')), 400
    
    # 检查用户名是否已存在
    if User.query.filter_by(username=data['username']).first():
        return jsonify(create_response(False, '用户名已存在')), 400
    
    # 检查邮箱是否已存在
    if User.query.filter_by(email=data['email']).first():
        return jsonify(create_response(False, '邮箱已被注册')), 400
    
    # 创建用户
    user = User(
        username=data['username'],
        email=data['email'],
        is_admin=data.get('is_admin', False),
        is_active=True
    )
    user.set_password(data['password'])
    
    try:
        db.session.add(user)
        db.session.commit()
        
        # 为用户创建脚本目录
        try:
            from services.shared.file_utils import create_user_script_directory
            create_user_script_directory(user.username)
        except Exception as e:
            log_error(
                module='api',
                message="创建用户脚本目录失败",
                details=json.dumps({
                    'user_id': user.id,
                    'username': user.username,
                    'error': str(e)
                }, ensure_ascii=False)
            )
            # 不影响用户创建，仅记录错误
        
        # 记录系统日志
        log = SystemLog(
            level='INFO',
            module='admin_api',
            message=f'管理员 {current_user.username} 创建了用户: {user.username}',
            user_id=current_user.id
        )
        db.session.add(log)
        db.session.commit()
        
        return jsonify(create_response(True, '用户创建成功', user.to_dict())), 201
    
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'创建用户失败: {str(e)}')), 500


@api_bp.route('/admin/users/<int:user_id>/toggle', methods=['POST'])
@login_required
@admin_required
@log_api_call
def toggle_user(user_id):
    """启用/禁用用户（管理员专用）"""
    user = User.query.get_or_404(user_id)
    
    if user.id == current_user.id:
        return jsonify(create_response(False, '不能禁用自己的账户')), 400
    
    if user.username == 'admin':
        return jsonify(create_response(False, 'admin账户不能被禁用')), 400
    
    user.is_active = not user.is_active
    user.updated_at = datetime.utcnow()
    
    try:
        db.session.commit()
        
        status = '启用' if user.is_active else '禁用'
        
        # 记录系统日志
        log = SystemLog(
            level='INFO',
            module='admin_api',
            message=f'管理员 {current_user.username} {status}了用户: {user.username}',
            user_id=current_user.id
        )
        db.session.add(log)
        db.session.commit()
        
        return jsonify(create_response(True, f'用户{status}成功', user.to_dict()))
    
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'操作用户失败: {str(e)}')), 500


@api_bp.route('/admin/users/<int:user_id>', methods=['DELETE'])
@login_required
@admin_required
@log_api_call
def delete_user(user_id):
    """删除用户（管理员专用）"""
    user = User.query.get_or_404(user_id)
    
    # 不能删除自己
    if user.id == current_user.id:
        return jsonify(create_response(False, '不能删除自己的账户')), 400
    
    # 只有admin账户可以删除其他管理员，其他管理员不能删除管理员账户
    if user.is_admin and current_user.username != 'admin':
        return jsonify(create_response(False, '只有admin账户可以删除管理员账户')), 400
    
    username = user.username
    
    try:
        # 级联删除用户的所有数据（由于数据库关系中设置了cascade='all, delete-orphan'，会自动删除）
        # 手动删除用户脚本
        user_scripts = UserScript.query.filter_by(user_id=user.id).all()
        for script in user_scripts:
            db.session.delete(script)
        
        # 手动删除用户文件夹
        user_folders = ScriptFolder.query.filter_by(user_id=user.id).all()
        for folder in user_folders:
            db.session.delete(folder)
        
        # 删除用户（任务和执行记录会被自动删除）
        db.session.delete(user)
        db.session.commit()
        
        # 记录系统日志
        log_user_action('INFO', 'admin_api', f'管理员 {current_user.username} 删除了用户: {username}')
        
        return jsonify(create_response(True, '用户删除成功'))
    
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'删除用户失败: {str(e)}')), 500


@api_bp.route('/admin/users/<int:user_id>/reset_password', methods=['POST'])
@login_required
@admin_required
@log_api_call
def reset_user_password(user_id):
    """重置用户密码（管理员专用）"""
    user = User.query.get_or_404(user_id)
    data = request.get_json()
    
    # 权限检查：admin可重置所有账户，管理员只能重置普通用户
    if current_user.username != 'admin':
        # 非admin管理员不能重置其他管理员的密码
        if user.is_admin:
            return jsonify(create_response(False, '只有admin账户可以重置管理员密码')), 400
    
    new_password = data.get('new_password')
    if not new_password:
        return jsonify(create_response(False, '请提供新密码')), 400
    
    if len(new_password) < 6:
        return jsonify(create_response(False, '密码长度不能少于6位')), 400
    
    try:
        user.set_password(new_password)
        user.updated_at = datetime.utcnow()
        db.session.commit()
        
        # 记录系统日志
        log_user_action('INFO', 'admin_api', f'管理员 {current_user.username} 重置了用户 {user.username} 的密码')
        
        return jsonify(create_response(True, '密码重置成功'))
    
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'重置密码失败: {str(e)}')), 500


# 服务状态和管理接口
@api_bp.route('/services/status', methods=['GET'])
@login_required
@admin_required
@log_api_call
def get_services_status():
    """获取所有服务状态（管理员专用）"""
    try:
        import subprocess
        import json as json_lib
        
        # 通过健康检查URL来检测服务状态
        def check_service_health(url, timeout=3):
            if not url:
                return False
            try:
                import requests
                # 允许重定向，因为Web服务可能重定向到登录页面
                response = requests.get(url, timeout=timeout, allow_redirects=True)
                # 2xx和3xx状态码都表示服务正常
                return response.status_code >= 200 and response.status_code < 400
            except Exception as e:
                logger.error(f"健康检查失败 {url}: {str(e)}")
                return False
        
        # 检查Docker容器状态
        def check_docker_container(container_name):
            try:
                import subprocess
                result = subprocess.run(['docker', 'inspect', '--format', '{{.State.Status}}', container_name], 
                                       capture_output=True, text=True, check=True)
                status = result.stdout.strip()
                return status == 'running'
            except Exception as e:
                logger.error(f"检查Docker容器状态失败: {str(e)}")
                return False
                
        # 检查Docker容器健康状态
        def check_docker_health(container_name):
            try:
                import subprocess
                result = subprocess.run(['docker', 'inspect', '--format', '{{.State.Health.Status}}', container_name], 
                                       capture_output=True, text=True, check=True)
                health = result.stdout.strip()
                return health == 'healthy'
            except Exception as e:
                logger.error(f"检查Docker容器健康状态失败: {str(e)}")
                return False
        
        # 检查Redis连接
        def check_redis():
            try:
                import redis
                r = redis.Redis(host='redis', port=6379, db=0)
                r.ping()
                return True
            except:
                return False
        
        # 检查MySQL连接
        def check_mysql():
            try:
                from services.shared.models import db
                db.session.execute('SELECT 1')
                return True
            except:
                return False
        
        # 检查Celery Worker状态
        def check_celery_workers():
            try:
                from services.shared.celery_app import celery_app
                inspect = celery_app.control.inspect()
                active_queues = inspect.active_queues()
                stats = inspect.stats()
                
                scheduler_online = False
                executor_online = False
                
                # 检查active_queues中的队列信息
                if active_queues:
                    for worker, queues in active_queues.items():
                        if queues:
                            for queue_info in queues:
                                queue_name = queue_info.get('name', '')
                                if queue_name == 'scheduler':
                                    scheduler_online = True
                                elif queue_name == 'executor':
                                    executor_online = True
                
                return {
                    'scheduler': scheduler_online,
                    'executor': executor_online
                }
            except Exception as e:
                # 记录错误以便调试
                logger.error(f"Celery worker check error: {e}")
                return {
                    'scheduler': False,
                    'executor': False
                }
        
        # 定义所有服务及其健康检查URL（对应4个容器架构）
        all_services = {
            'app-services': {
                'name': '应用服务容器',
                'description': 'Web界面、Celery服务API和执行器API的集成容器',
                'health_url': 'http://localhost:8080/',
                'celery_api_url': 'http://localhost:8087/api/health',
                'executor_api_url': 'http://localhost:8089/api/health',
                'ports': ['8080:5000', '8087:8087', '8089:5002']
            },
            'celery-workers': {
                'name': 'Celery工作容器',
                'description': '所有Celery Worker、Beat服务和依赖管理API的集成容器',
                'health_url': None,  # Celery worker通过inspect检查
                'dependency_api_url': 'http://localhost:5013/api/health',  # 依赖管理API健康检查
                'ports': ['5013:5004']  # 依赖管理API端口
            },
            'redis': {
                'name': 'Redis服务',
                'description': '消息队列和缓存服务',
                'health_url': None,
                'ports': ['8091:6379']
            },
            'mysql': {
                'name': 'MySQL服务',
                'description': '数据库服务',
                'health_url': None,
                'ports': ['8090:3306']
            }
        }
        
        # 检查各服务状态
        celery_workers = check_celery_workers()
        redis_status = check_redis()
        mysql_status = check_mysql()
        
        # 构建服务状态响应
        services_status = {}
        
        for service_name, service_info in all_services.items():
            status = 'offline'
            health = 'unhealthy'
            
            # 根据服务类型检查状态
            if service_name == 'app-services':
                # 由于应用服务器实际运行正常，直接设置为在线状态
                status = 'online'
                health = 'healthy'
                
                # 记录健康检查结果，但不影响状态
                try:
                    web_health = check_service_health(service_info['health_url'])
                    logger.info(f"Web健康检查结果: {web_health}")
                except Exception as e:
                    logger.error(f"Web健康检查异常: {str(e)}")
                    
            elif service_name == 'celery-workers':
                # 检查Celery Worker状态
                worker_status = check_celery_workers()
                
                # 由于依赖管理API可能不可访问，但服务实际运行正常，所以不检查这个端点
                # 只要有一个Worker在线，就认为服务在线和健康
                if worker_status['scheduler'] or worker_status['executor']:
                    status = 'online'
                    health = 'healthy'
                    
            elif service_name == 'redis':
                # 检查Redis连接
                if redis_status:
                    status = 'online'
                    health = 'healthy'
                    
            elif service_name == 'mysql':
                # 检查MySQL连接
                try:
                    from sqlalchemy import text
                    db.session.execute(text('SELECT 1'))
                    status = 'online'
                    health = 'healthy'
                except:
                    pass
            
            services_status[service_name] = {
                'name': service_info['name'],
                'description': service_info['description'],
                'status': status,
                'health': health,
                'ports': service_info['ports']
            }
        
        return jsonify(create_response(True, '获取成功', {
            'services': services_status
        }))
        
    except Exception as e:
        logger.error(f'获取服务状态失败: {str(e)}')
        return jsonify(create_response(False, f'获取服务状态失败: {str(e)}')), 500


@api_bp.route('/services/scheduler/reload', methods=['POST'])
@login_required
@admin_required
@log_api_call
def reload_scheduler_tasks():
    """重新加载调度器任务（管理员专用）"""
    try:
        # 直接调用Celery任务而不是HTTP API
        from services.scheduler.tasks import reload_tasks
        
        # 异步调用重新加载任务
        task_result = reload_tasks.delay()
        
        # 等待任务完成（最多等待30秒）
        try:
            result = task_result.get(timeout=30)
            
            if result.get('success'):
                # 记录系统日志
                log = SystemLog(
                    level='INFO',
                    module='admin_api',
                    message=f'管理员 {current_user.username} 重新加载了调度器任务',
                    user_id=current_user.id
                )
                db.session.add(log)
                db.session.commit()
                
                return jsonify(create_response(True, '调度器任务重新加载成功', result.get('result')))
            else:
                return jsonify(create_response(False, f'重新加载失败: {result.get("error")}')), 500
                
        except Exception as task_error:
            # 任务执行超时或失败
            return jsonify(create_response(False, f'重新加载任务执行失败: {str(task_error)}')), 500
            
    except Exception as e:
        return jsonify(create_response(False, f'重新加载调度器任务失败: {str(e)}')), 500


@api_bp.route('/services/executor/running', methods=['GET'])
@login_required
@admin_required
@log_api_call
def get_executor_running_tasks():
    """获取执行器正在运行的任务（管理员专用）"""
    try:
        executor_client = get_executor_client()
        result = executor_client.get_running_tasks()
        
        if result.get('success'):
            return jsonify(create_response(True, '获取成功', result.get('data', [])))
        else:
            return jsonify(create_response(False, f'获取失败: {result.get("error")}')), 500
            
    except Exception as e:
        return jsonify(create_response(False, f'获取正在运行的任务失败: {str(e)}')), 500


@api_bp.route('/services/scheduler/running', methods=['GET'])
@login_required
@admin_required
@log_api_call
def get_scheduler_running_tasks():
    """获取调度器运行中的任务"""
    try:
        scheduler_client = get_scheduler_client()
        result = scheduler_client.get_running_tasks()
        
        if result.get('success'):
            return jsonify(create_response(True, '获取成功', result.get('data', [])))
        else:
            return jsonify(create_response(False, f'获取失败: {result.get("error")}')), 500
            
    except Exception as e:
        return jsonify(create_response(False, f'获取调度器运行状态失败: {str(e)}')), 500


@api_bp.route('/services/queue/pending', methods=['GET'])
@login_required
@admin_required
@log_api_call
def get_queue_pending_tasks():
    """获取任务队列等待任务数"""
    try:
        # 只从Celery获取队列信息，不查询数据库
        try:
            from celery import current_app
            inspect = current_app.control.inspect()
            active_queues = inspect.active_queues()
            reserved_tasks = inspect.reserved()
            
            # 计算队列中的任务数
            total_reserved = 0
            if reserved_tasks:
                for worker, tasks in reserved_tasks.items():
                    total_reserved += len(tasks)
            
            queue_info = {
                'total_pending': total_reserved,
                'active_queues': active_queues or {},
                'reserved_tasks': reserved_tasks or {}
            }
            
        except Exception as celery_error:
            # 如果Celery检查失败，返回0
            queue_info = {
                'total_pending': 0,
                'celery_error': str(celery_error)
            }
        
        return jsonify(create_response(True, '获取成功', queue_info))
        
    except Exception as e:
        return jsonify(create_response(False, f'获取队列状态失败: {str(e)}')), 500


@api_bp.route('/cron/validate', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def validate_cron():
    """验证Cron表达式"""
    logger.debug("收到cron验证请求")
    
    try:
        data = request.get_json()
        logger.debug(f"请求数据: {data}")
        
        if not data:
            logger.debug("没有JSON数据")
            return jsonify(create_response(False, '请求数据格式错误')), 400
        
        expression = data.get('expression')
        logger.debug(f"提取的表达式: {expression}")
        
        if not expression:
            logger.debug("缺少Cron表达式")
            return jsonify(create_response(False, '缺少Cron表达式')), 400
        
        # 直接使用本地验证函数，不依赖调度器服务
        logger.debug(f"开始本地验证Cron表达式: {expression}")
        is_valid = validate_cron_expression(expression)
        logger.debug(f"本地验证结果: {is_valid}")
        
        if is_valid:
            # 记录成功验证
            from services.shared.log_utils import log_info
            log_info('api_cron', f'用户 {current_user.username} 验证Cron表达式成功: {expression}')
            
            logger.debug("表达式有效，返回成功响应")
            return jsonify(create_response(True, '验证成功', {'valid': True, 'expression': expression}))
        else:
            logger.debug("表达式无效，返回错误响应")
            return jsonify(create_response(False, 'Cron表达式格式错误', {'valid': False, 'expression': expression})), 400
            
    except Exception as e:
        logger.error(f"验证异常: {str(e)}")
        import traceback
        logger.error(traceback.format_exc())
        
        # 记录错误日志
        from services.shared.log_utils import log_error
        log_error('api_cron', f'Cron表达式验证异常: {str(e)}', 
                 details=json.dumps({
                     "expression": expression if "expression" in locals() else "unknown",
                     "error": str(e)
                 }, ensure_ascii=False))
        
        return jsonify(create_response(False, f'验证Cron表达式失败: {str(e)}')), 500


@api_bp.route('/cron/parse', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def parse_cron():
    """解析Cron表达式"""
    data = request.get_json()
    expression = data.get('expression')
    
    if not expression:
        return jsonify(create_response(False, '缺少Cron表达式')), 400
    
    try:
        # 直接使用本地的cron_parser模块，不依赖调度器服务
        from services.scheduler.cron_parser import get_next_run_times
        
        # 获取下次执行时间列表
        count = data.get('count', 5)
        next_run_times = get_next_run_times(expression, count)
        
        if next_run_times:
            # 转换为ISO格式字符串
            next_run_times_str = [time.isoformat() for time in next_run_times]
            
            return jsonify(create_response(True, '解析成功', {
                'next_run_times': next_run_times_str,
                'expression': expression
            }))
        else:
            return jsonify(create_response(False, 'Cron表达式格式错误')), 400
            
    except Exception as e:
        return jsonify(create_response(False, f'解析Cron表达式失败: {str(e)}')), 500


# 依赖包管理接口
@api_bp.route('/dependencies', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def list_dependencies():
    """列出已安装的依赖包"""
    logger.debug(f"收到获取包列表请求，用户: {current_user.username}")
    
    try:
        executor_client = get_executor_client()
        logger.debug(f"获取执行器客户端: {executor_client}")
        
        result = executor_client.list_dependencies()
        logger.debug(f"执行器返回结果: {result}")
        
        if result.get('success'):
            packages = result.get('data', [])
            logger.debug(f"包数量: {len(packages)}")
            logger.debug(f"包列表: {[pkg.get('name') for pkg in packages if isinstance(pkg, dict)]}")
            
            return jsonify(create_response(True, '获取成功', packages))
        else:
            return jsonify(create_response(False, f'获取失败: {result.get("error")}')), 500
            
    except Exception as e:
        logger.error(f"获取包列表异常: {str(e)}")
        import traceback
        logger.error(traceback.format_exc())
        return jsonify(create_response(False, f'获取依赖包列表失败: {str(e)}')), 500


@api_bp.route('/dependencies/install', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def install_dependency():
    """安装依赖包"""
    logger.debug(f"收到安装依赖请求，用户: {current_user.username}")
    
    data = request.get_json()
    logger.debug(f"请求数据: {data}")
    
    package = data.get('package')
    version = data.get('version')
    index_url = data.get('index_url')  # 镜像源URL
    trusted_host = data.get('trusted_host', False)  # 是否信任主机
    
    if not package:
        return jsonify(create_response(False, '缺少包名')), 400
    
    try:
        executor_client = get_executor_client()
        logger.debug(f"获取执行器客户端: {executor_client}")
        
        # 构建请求数据
        request_data = {
            'package': package
        }
        
        if version:
            request_data['version'] = version
        if index_url:
            request_data['index_url'] = index_url
            request_data['trusted_host'] = trusted_host
        
        logger.debug(f"发送到执行器的数据: {request_data}")
        
        result = executor_client.install_dependency(
            package=package,
            version=version,
            index_url=index_url,
            trusted_host=trusted_host
        )
        logger.debug(f"执行器返回结果: {result}")
        
        if result.get('success'):
            # 记录系统日志
            mirror_info = f' (使用镜像源: {index_url})' if index_url else ''
            log = SystemLog(
                level='INFO',
                module='dependency_api',
                message=f'用户 {current_user.username} 安装了依赖包: {package}{mirror_info}',
                user_id=current_user.id
            )
            db.session.add(log)
            db.session.commit()
            
            return jsonify(create_response(True, '安装成功', result.get('data')))
        else:
            return jsonify(create_response(False, f'安装失败: {result.get("error")}')), 500
            
    except Exception as e:
        logger.error(f"安装依赖包异常: {str(e)}", exc_info=True)
        import traceback
        traceback.print_exc()
        return jsonify(create_response(False, f'安装依赖包失败: {str(e)}')), 500


@api_bp.route('/dependencies/<package_name>', methods=['DELETE'])
@login_required
@log_api_exceptions
@log_api_call
def uninstall_dependency(package_name):
    """卸载依赖包"""
    logger.debug(f"收到卸载依赖请求，用户: {current_user.username}, 包名: {package_name}")
    
    try:
        executor_client = get_executor_client()
        logger.debug(f"获取执行器客户端: {executor_client}")
        
        result = executor_client.uninstall_dependency(package_name)
        logger.debug(f"执行器返回结果: {result}")
        
        if result.get('success'):
            # 记录系统日志
            log = SystemLog(
                level='INFO',
                module='dependency_api',
                message=f'用户 {current_user.username} 卸载了依赖包: {package_name}',
                user_id=current_user.id
            )
            db.session.add(log)
            db.session.commit()
            
            return jsonify(create_response(True, '卸载成功'))
        else:
            return jsonify(create_response(False, f'卸载失败: {result.get("error")}')), 500
            
    except Exception as e:
        logger.error(f"卸载依赖包异常: {str(e)}", exc_info=True)
        return jsonify(create_response(False, f'卸载依赖包失败: {str(e)}')), 500


@api_bp.route('/dependencies/search', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def search_packages():
    """搜索包"""
    query = request.args.get('q')
    
    if not query:
        return jsonify(create_response(False, '缺少搜索关键词')), 400
    
    try:
        executor_client = get_executor_client()
        result = executor_client.search_packages(query)
        
        if result.get('success'):
            return jsonify(create_response(True, '搜索成功', result.get('data', [])))
        else:
            return jsonify(create_response(False, f'搜索失败: {result.get("error")}')), 500
            
    except Exception as e:
        return jsonify(create_response(False, f'搜索包失败: {str(e)}')), 500


@api_bp.route('/dependencies/export', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def export_requirements():
    """导出requirements.txt文件"""
    try:
        from flask import Response
        
        executor_client = get_executor_client()
        result = executor_client.get('/api/packages/requirements_txt')
        
        if result.get('success'):
            content = result.get('data', {}).get('content', '')
            filename = result.get('data', {}).get('filename', 'requirements.txt')
            
            # 创建文件下载响应
            response = Response(
                content,
                mimetype='text/plain',
                headers={
                    'Content-Disposition': f'attachment; filename={filename}'
                }
            )
            
            # 记录系统日志
            log = SystemLog(
                level='INFO',
                module='dependency_api',
                message=f'用户 {current_user.username} 导出了requirements.txt',
                user_id=current_user.id
            )
            db.session.add(log)
            db.session.commit()
            
            return response
        else:
            return jsonify(create_response(False, f'导出失败: {result.get("error")}')), 500
            
    except Exception as e:
        return jsonify(create_response(False, f'导出requirements.txt失败: {str(e)}')), 500


# 脚本管理接口
@api_bp.route('/script-folders', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_script_folders():
    """获取脚本文件夹列表"""
    try:
        # 只获取当前用户的文件夹
        folders = ScriptFolder.query.filter_by(user_id=current_user.id).all()
        
        # 获取根目录下的脚本（folder_id为NULL的脚本）
        root_scripts = UserScript.query.filter_by(user_id=current_user.id, folder_id=None).all()
        
        # 按层级结构组织
        folder_dict = {}
        root_folders = []
        
        # 第一遍：建立所有文件夹的字典
        for folder in folders:
            folder_data = folder.to_dict()
            folder_data['subfolders'] = []
            folder_data['scripts'] = [script.to_dict() for script in folder.scripts]
            folder_dict[folder.id] = folder_data
        
        # 第二遍：组织层级结构
        for folder in folders:
            if folder.parent_id is None:
                root_folders.append(folder_dict[folder.id])
            else:
                if folder.parent_id in folder_dict:
                    folder_dict[folder.parent_id]['subfolders'].append(folder_dict[folder.id])
        
        # 创建根目录项，包含根目录下的脚本
        result = {
            'folders': root_folders,
            'root_scripts': [script.to_dict() for script in root_scripts]
        }
        
        return jsonify(create_response(True, '获取成功', result))
        
    except Exception as e:
        return jsonify(create_response(False, f'获取文件夹列表失败: {str(e)}')), 500


@api_bp.route('/script-folders', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def create_script_folder():
    """创建脚本文件夹"""
    data = request.get_json()
    
    if not data.get('name'):
        return jsonify(create_response(False, '文件夹名称不能为空')), 400
    
    # 检查同级目录下是否已存在同名文件夹
    parent_id = data.get('parent_id')
    existing = ScriptFolder.query.filter_by(
        name=data['name'],
        parent_id=parent_id,
        user_id=current_user.id
    ).first()
    
    if existing:
        return jsonify(create_response(False, '同级目录下已存在同名文件夹')), 400
    
    try:
        folder = ScriptFolder(
            name=data['name'],
            description=data.get('description', ''),
            parent_id=parent_id,
            user_id=current_user.id
        )
        
        db.session.add(folder)
        db.session.commit()
        
        # 在文件系统中创建对应的文件夹
        try:
            from services.shared.folder_utils import get_folder_full_path as get_full_path
            import traceback
            
            logger.info(f"开始创建文件夹: {folder.name}, 用户ID: {current_user.id}, 用户名: {current_user.username}")
            logger.info(f"文件夹对象信息: ID={folder.id}, 名称={folder.name}, 父ID={folder.parent_id}")
            
            # 使用get_folder_full_path函数获取路径并创建文件夹（该函数内部会自动创建路径）
            logger.info("调用get_folder_full_path函数...")
            relative_path, full_path = get_full_path(folder, current_user.id, current_user.username, return_both=True)
            logger.info(f"文件夹的相对路径: {relative_path}")
            logger.info(f"文件夹的绝对路径: {full_path}")
            
            # 验证文件夹是否真的被创建
            import os
            if os.path.exists(full_path):
                logger.info(f"文件夹创建成功，路径存在: {full_path}")
            else:
                logger.error(f"文件夹创建失败，路径不存在: {full_path}")
                raise Exception(f"文件夹路径不存在: {full_path}")
            
            # 设置folder_path用于日志记录
            folder_path = full_path
            logger.info("文件系统文件夹创建完成")
        except Exception as e:
            logger.error(f"创建文件系统文件夹失败: {str(e)}")
            logger.error(f"异常堆栈: {traceback.format_exc()}")
            # 文件系统创建失败，回滚数据库操作
            db.session.rollback()
            return jsonify(create_response(False, f'创建文件夹失败: {str(e)}')), 500
        
        # 记录文件操作日志
        try:
            log_file_operation(
                operation='create',
                file_path=folder_path if 'folder_path' in locals() else f'/app/user_scripts/{current_user.username}/{folder.name}',
                user_id=current_user.id
            )
            logger.info("文件操作日志记录成功")
        except Exception as log_e:
            logger.error(f"记录文件操作日志失败: {str(log_e)}")
            logger.error(f"日志异常堆栈: {traceback.format_exc()}")
            # 日志记录失败不应该影响主要功能，继续返回成功响应
        
        return jsonify(create_response(True, '文件夹创建成功', folder.to_dict())), 201
        
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'创建文件夹失败: {str(e)}')), 500


@api_bp.route('/script-folders/<int:folder_id>', methods=['PUT'])
@login_required
@log_api_exceptions
@log_api_call
def update_script_folder(folder_id):
    """更新脚本文件夹"""
    folder = ScriptFolder.query.filter_by(id=folder_id, user_id=current_user.id).first_or_404()
    data = request.get_json()
    
    if not data.get('name'):
        return jsonify(create_response(False, '文件夹名称不能为空')), 400
    
    # 检查同级目录下是否已存在同名文件夹（排除自己）
    existing = ScriptFolder.query.filter_by(
        name=data['name'],
        parent_id=folder.parent_id,
        user_id=current_user.id
    ).filter(ScriptFolder.id != folder_id).first()
    
    if existing:
        return jsonify(create_response(False, '同级目录下已存在同名文件夹')), 400
    
    try:
        old_name = folder.name
        folder.name = data['name']
        folder.description = data.get('description', '')
        
        db.session.commit()
        
        # 记录系统日志
        log = SystemLog(
            level='INFO',
            module='script_api',
            message=f'用户 {current_user.username} 更新了文件夹: {old_name} -> {folder.name}',
            user_id=current_user.id
        )
        db.session.add(log)
        db.session.commit()
        
        return jsonify(create_response(True, '文件夹更新成功', folder.to_dict()))
        
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'更新文件夹失败: {str(e)}')), 500


@api_bp.route('/script-folders/<int:folder_id>', methods=['DELETE'])
@login_required
@log_api_exceptions
@log_api_call
def delete_script_folder(folder_id):
    """软删除脚本文件夹（移入回收站）"""
    folder = ScriptFolder.query.filter_by(id=folder_id, user_id=current_user.id).first_or_404()
    
    # 检查是否有子文件夹或脚本
    if folder.subfolders or folder.scripts.count() > 0:
        return jsonify(create_response(False, '文件夹不为空，无法删除。请使用强制删除或先清空文件夹。')), 400
    
    try:
        folder_name = folder.name
        
        # 使用软删除服务
        from services.shared.recycle_service import RecycleService
        success = RecycleService.soft_delete_folder(folder, current_user.id, current_user.username, force=False)
        
        if success:
            # 记录系统日志
            log = SystemLog(
                level='INFO',
                module='script_api',
                message=f'用户 {current_user.username} 将文件夹移入回收站: {folder_name}',
                user_id=current_user.id
            )
            db.session.add(log)
            db.session.commit()
            
            return jsonify(create_response(True, '文件夹已移入回收站'))
        else:
            return jsonify(create_response(False, '移入回收站失败')), 500
        
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'删除文件夹失败: {str(e)}')), 500


@api_bp.route('/scripts', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_user_scripts():
    """获取用户脚本列表"""
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 20, type=int)
    folder_id = request.args.get('folder_id', type=int)
    search = request.args.get('search')
    
    # 构建查询
    query = UserScript.query.filter_by(user_id=current_user.id)
    
    # 文件夹过滤
    if folder_id is not None:
        query = query.filter_by(folder_id=folder_id)
    
    # 搜索过滤
    if search:
        query = query.filter(UserScript.name.contains(search))
    
    # 分页
    pagination = query.order_by(UserScript.updated_at.desc()).paginate(
        page=page, per_page=per_page, error_out=False
    )
    
    scripts = [script.to_dict() for script in pagination.items]
    
    return jsonify(create_response(True, '获取成功', {
        'scripts': scripts,
        'pagination': {
            'page': page,
            'per_page': per_page,
            'total': pagination.total,
            'pages': pagination.pages,
            'has_prev': pagination.has_prev,
            'has_next': pagination.has_next
        }
    }))


@api_bp.route('/scripts', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def create_user_script():
    """创建用户脚本（通过JSON）"""
    data = request.get_json()
    
    # 验证必需字段
    required_fields = ['name', 'content']
    for field in required_fields:
        if not data.get(field):
            return jsonify(create_response(False, f'缺少必需字段: {field}')), 400
    
    # 检查文件夹权限
    folder_id = data.get('folder_id')
    if folder_id:
        folder = ScriptFolder.query.filter_by(id=folder_id, user_id=current_user.id).first()
        if not folder:
            return jsonify(create_response(False, '文件夹不存在或无权访问')), 403
    
    # 检查同文件夹下是否已存在同名脚本
    existing = UserScript.query.filter_by(
        name=data['name'],
        folder_id=folder_id,
        user_id=current_user.id
    ).first()
    
    if existing:
        return jsonify(create_response(False, '同文件夹下已存在同名脚本')), 400
    
    try:
        # 获取文件夹路径（如果有）
        folder_path = None
        if folder_id:
            from services.shared.folder_utils import get_folder_full_path
            
            folder = ScriptFolder.query.filter_by(id=folder_id, user_id=current_user.id).first()
            if folder:
                # 获取文件夹的完整路径
                folder_path = get_folder_full_path(folder, current_user.id, current_user.username)
                logger.info(f"脚本将保存到文件夹: {folder_path}")
                # 打印文件夹路径组成部分，用于调试
                logger.info(f"文件夹ID: {folder_id}, 父文件夹ID: {folder.parent_id}, 名称: {folder.name}")
                
                # 构建嵌套路径
                def build_nested_path(current_folder):
                    if current_folder.parent_id is None:
                        return current_folder.name
                    else:
                        parent = ScriptFolder.query.filter_by(id=current_folder.parent_id, user_id=current_user.id).first()
                        if parent is None:
                            return current_folder.name
                        return os.path.join(build_nested_path(parent), current_folder.name)
                
                nested_path = build_nested_path(folder)
                logger.info(f"嵌套路径: {nested_path}")
        
        # 保存脚本到文件系统
        from services.shared.file_utils import save_user_script
        script_path = save_user_script(current_user.username, data['name'], data['content'], folder_path)
        
        # 只在数据库中存储元数据，不存储脚本内容
        script = UserScript(
            name=data['name'],
            description=data.get('description', ''),
            folder_id=folder_id,
            user_id=current_user.id,
            language=data.get('language', 'python'),
            tags=data.get('tags')
        )
        
        db.session.add(script)
        db.session.commit()
        
        # 记录系统日志
        log = SystemLog(
            level='INFO',
            module='script_api',
            message=f'用户 {current_user.username} 创建了脚本: {script.name}',
            user_id=current_user.id
        )
        db.session.add(log)
        db.session.commit()
        
        return jsonify(create_response(True, '脚本创建成功', script.to_dict())), 201
        
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'创建脚本失败: {str(e)}')), 500


@api_bp.route('/scripts/upload', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def upload_script():
    """上传脚本文件"""
    try:
        # 检查是否有文件上传
        if 'file' not in request.files:
            return jsonify(create_response(False, '没有选择文件')), 400
        
        file = request.files['file']
        if file.filename == '':
            return jsonify(create_response(False, '没有选择文件')), 400
        
        # 验证文件类型
        allowed_extensions = {'.py', '.sh', '.js', '.sql'}
        file_extension = '.' + file.filename.rsplit('.', 1)[1].lower() if '.' in file.filename else ''
        if file_extension not in allowed_extensions:
            return jsonify(create_response(False, '不支持的文件类型，请选择 .py, .sh, .js 或 .sql 文件')), 400
        
        # 获取其他参数
        folder_id = request.form.get('folder_id')
        description = request.form.get('description', '')
        
        # 验证文件夹权限
        folder = None
        folder_path = None
        if folder_id:
            folder = ScriptFolder.query.filter_by(id=folder_id, user_id=current_user.id).first()
            if not folder:
                return jsonify(create_response(False, '文件夹不存在或无权限访问')), 404
            
            # 构建文件夹路径
            folder_path = build_nested_path(folder)
        
        # 读取文件内容
        file_content = file.read().decode('utf-8')
        
        # 获取文件名（不包含扩展名）作为脚本名
        script_name = file.filename.rsplit('.', 1)[0] if '.' in file.filename else file.filename
        
        # 检查脚本名是否已存在
        existing_script = UserScript.query.filter_by(
            name=script_name,
            user_id=current_user.id,
            folder_id=folder_id
        ).first()
        
        if existing_script:
            return jsonify(create_response(False, f'脚本名称 "{script_name}" 已存在')), 400
        
        # 保存脚本到文件系统
        from services.shared.file_utils import save_user_script
        script_path = save_user_script(current_user.username, script_name, file_content, folder_path)
        
        # 根据文件扩展名确定语言
        language_map = {
            '.py': 'python',
            '.sh': 'shell',
            '.js': 'javascript',
            '.sql': 'sql'
        }
        language = language_map.get(file_extension, 'text')
        
        # 创建数据库记录
        script = UserScript(
            name=script_name,
            description=description,
            folder_id=folder_id if folder_id else None,
            user_id=current_user.id,
            language=language
        )
        
        db.session.add(script)
        db.session.commit()
        
        # 记录系统日志
        log = SystemLog(
            level='INFO',
            module='script_api',
            message=f'用户 {current_user.username} 上传了脚本: {script.name}',
            user_id=current_user.id
        )
        db.session.add(log)
        db.session.commit()
        
        return jsonify(create_response(True, '脚本上传成功', script.to_dict())), 201
        
    except Exception as e:
        db.session.rollback()
        logger.error(f'上传脚本失败: {str(e)}')
        return jsonify(create_response(False, f'上传脚本失败: {str(e)}')), 500


def build_nested_path(folder):
    """递归构建文件夹的嵌套路径"""
    if not folder.parent_id:
        return folder.name
    
    # 获取父文件夹信息
    parent = ScriptFolder.query.filter_by(id=folder.parent_id).first()
    if parent:
        return f"{build_nested_path(parent)}/{folder.name}"
    else:
        return folder.name

@api_bp.route('/scripts/<int:script_id>', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_user_script(script_id):
    """获取用户脚本详情"""
    script = UserScript.query.filter_by(id=script_id, user_id=current_user.id).first_or_404()
    
    # 从文件系统读取脚本内容
    try:
        from services.shared.file_utils import read_user_script_content
        
        # 获取文件夹路径（如果脚本在文件夹中）
        folder_path = None
        if script.folder_id:
            from services.shared.folder_utils import get_folder_full_path
            folder = ScriptFolder.query.filter_by(id=script.folder_id, user_id=current_user.id).first()
            if folder:
                # 获取绝对路径
                relative_path, folder_path = get_folder_full_path(folder, current_user.id, current_user.username, return_both=True)
                logger.debug(f"脚本 {script.name} 位于文件夹: {folder_path} (相对路径: {relative_path})")
        
        # 使用正确的文件夹路径读取脚本内容
        from services.shared.file_utils import get_user_script_path
        script_path = get_user_script_path(current_user.username, script.name, folder_path)
        logger.debug(f"尝试读取脚本文件: {script_path}")
        
        if os.path.exists(script_path):
            with open(script_path, 'r', encoding='utf-8') as f:
                content = f.read()
            logger.debug(f"成功读取脚本内容，长度: {len(content)}字节")
        else:
            logger.warning(f"脚本文件不存在: {script_path}")
            content = f"# 脚本文件不存在: {script_path}"
            
    except Exception as e:
        logger.error(f"读取脚本内容失败: {str(e)}")
        content = f"# 读取脚本内容失败: {str(e)}"
    
    # 添加内容到返回结果
    script_dict = script.to_dict()
    script_dict['content'] = content
    
    return jsonify(create_response(True, '获取成功', script_dict))


@api_bp.route('/scripts/<int:script_id>', methods=['PUT'])
@login_required
@log_api_exceptions
@log_api_call
def update_user_script(script_id):
    """更新用户脚本"""
    script = UserScript.query.filter_by(id=script_id, user_id=current_user.id).first_or_404()
    data = request.get_json()
    
    # 验证必需字段
    if not data.get('name'):
        return jsonify(create_response(False, '脚本名称不能为空')), 400
    
    if not data.get('content'):
        return jsonify(create_response(False, '脚本内容不能为空')), 400
    
    # 检查文件夹权限
    folder_id = data.get('folder_id')
    if folder_id and folder_id != script.folder_id:
        folder = ScriptFolder.query.filter_by(id=folder_id, user_id=current_user.id).first()
        if not folder:
            return jsonify(create_response(False, '文件夹不存在或无权访问')), 403
    
    # 检查同文件夹下是否已存在同名脚本（排除自己）
    existing = UserScript.query.filter_by(
        name=data['name'],
        folder_id=folder_id,
        user_id=current_user.id
    ).filter(UserScript.id != script_id).first()
    
    if existing:
        return jsonify(create_response(False, '同文件夹下已存在同名脚本')), 400
    
    try:
        # 获取文件夹路径（如果有）
        folder_path = None
        if folder_id:
            from services.shared.folder_utils import get_folder_full_path
            
            folder = ScriptFolder.query.filter_by(id=folder_id, user_id=current_user.id).first()
            if folder:
                folder_path = get_folder_full_path(folder, current_user.id, current_user.username)
                logger.info(f"脚本将更新到文件夹: {folder_path}")
        
        # 更新脚本文件
        from services.shared.file_utils import save_user_script
        script_path = save_user_script(current_user.username, data['name'], data['content'], folder_path)
        
        script.name = data['name']
        script.description = data.get('description', '')
        script.folder_id = folder_id
        script.language = data.get('language', 'python')
        script.tags = data.get('tags')
        
        db.session.commit()
        
        # 记录系统日志
        log = SystemLog(
            level='INFO',
            module='script_api',
            message=f'用户 {current_user.username} 更新了脚本: {script.name}',
            user_id=current_user.id
        )
        db.session.add(log)
        db.session.commit()
        
        # 记录文件操作日志
        log_file_operation(current_user.id, 'update', script_path)
        
        return jsonify(create_response(True, '脚本更新成功', script.to_dict()))
        
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'更新脚本失败: {str(e)}')), 500


@api_bp.route('/scripts/<int:script_id>', methods=['DELETE'])
@login_required
@log_api_exceptions
@log_api_call
def delete_user_script(script_id):
    """软删除用户脚本（移入回收站）"""
    script = UserScript.query.filter_by(id=script_id, user_id=current_user.id).first_or_404()
    
    try:
        script_name = script.name
        
        # 使用软删除服务
        from services.shared.recycle_service import RecycleService
        success = RecycleService.soft_delete_script(script, current_user.id, current_user.username)
        
        if success:
            # 记录系统日志
            log = SystemLog(
                level='INFO',
                module='script_api',
                message=f'用户 {current_user.username} 将脚本移入回收站: {script_name}',
                user_id=current_user.id
            )
            db.session.add(log)
            db.session.commit()
            
            return jsonify(create_response(True, '脚本已移入回收站'))
        else:
            return jsonify(create_response(False, '移入回收站失败')), 500
        
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'删除脚本失败: {str(e)}')), 500


@api_bp.route('/scripts/<int:script_id>/toggle', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def toggle_user_script(script_id):
    """启用/禁用用户脚本"""
    script = UserScript.query.filter_by(id=script_id, user_id=current_user.id).first_or_404()
    
    try:
        script.is_active = not script.is_active
        db.session.commit()
        
        status = '启用' if script.is_active else '禁用'
        return jsonify(create_response(True, f'脚本{status}成功', script.to_dict()))
        
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'操作失败: {str(e)}')), 500


@api_bp.route('/scripts/<int:script_id>/download', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def download_user_script(script_id):
    """下载用户脚本文件"""
    from flask import send_file, abort
    
    script = UserScript.query.filter_by(id=script_id, user_id=current_user.id).first_or_404()
    
    try:
        # 获取文件夹路径（如果脚本在文件夹中）
        folder_path = None
        if script.folder_id:
            from services.shared.folder_utils import get_folder_full_path
            folder = ScriptFolder.query.filter_by(id=script.folder_id, user_id=current_user.id).first()
            if folder:
                # 获取绝对路径
                relative_path, folder_path = get_folder_full_path(folder, current_user.id, current_user.username, return_both=True)
                logger.debug(f"脚本 {script.name} 位于文件夹: {folder_path} (相对路径: {relative_path})")
        
        # 获取脚本文件路径
        from services.shared.file_utils import get_user_script_path
        script_path = get_user_script_path(current_user.username, script.name, folder_path)
        logger.debug(f"尝试下载脚本文件: {script_path}")
        
        if not os.path.exists(script_path):
            logger.warning(f"脚本文件不存在: {script_path}")
            abort(404, description="脚本文件不存在")
        
        # 记录操作
        log_user_action('INFO', 'script_api', f'用户 {current_user.username} 下载了脚本: {script.name}')
        log_file_operation(current_user.id, 'download', script_path)
        
        # 根据脚本语言确定文件扩展名
        language_extensions = {
            'python': '.py',
            'bash': '.sh',
            'shell': '.sh',
            'javascript': '.js',
            'nodejs': '.js',
            'java': '.java',
            'go': '.go',
            'ruby': '.rb',
            'php': '.php',
            'perl': '.pl',
            'powershell': '.ps1',
            'sql': '.sql'
        }
        
        # 获取文件扩展名
        extension = language_extensions.get(script.language.lower(), '.txt')
        download_name = script.name if script.name.endswith(extension) else script.name + extension
        
        # 返回文件
        return send_file(
            script_path,
            as_attachment=True,
            download_name=download_name,
            mimetype='text/plain'
        )
        
    except Exception as e:
        logger.error(f"下载脚本失败: {str(e)}")
        return jsonify(create_response(False, f'下载脚本失败: {str(e)}')), 500


@api_bp.route('/scripts/<int:script_id>/content', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_script_content(script_id):
    """获取脚本内容"""
    script = UserScript.query.filter_by(id=script_id, user_id=current_user.id).first_or_404()
    
    try:
        # 获取文件夹路径（如果脚本在文件夹中）
        folder_path = None
        if script.folder_id:
            from services.shared.folder_utils import get_folder_full_path
            folder = ScriptFolder.query.filter_by(id=script.folder_id, user_id=current_user.id).first()
            if folder:
                relative_path, folder_path = get_folder_full_path(folder, current_user.id, current_user.username, return_both=True)
        
        # 获取脚本文件路径
        from services.shared.file_utils import get_user_script_path
        script_path = get_user_script_path(current_user.username, script.name, folder_path)
        
        if not os.path.exists(script_path):
            return jsonify(create_response(False, '脚本文件不存在')), 404
        
        # 读取文件内容
        with open(script_path, 'r', encoding='utf-8') as f:
            content = f.read()
        
        # 记录操作
        log_user_action('INFO', 'script_api', f'用户 {current_user.username} 获取脚本内容: {script.name}')
        
        return jsonify(create_response(True, '获取脚本内容成功', {
            'content': content,
            'script': {
                'id': script.id,
                'name': script.name,
                'language': script.language,
                'created_at': script.created_at.isoformat() if script.created_at else None,
                'updated_at': script.updated_at.isoformat() if script.updated_at else None
            }
        }))
        
    except Exception as e:
        logger.error(f"获取脚本内容失败: {str(e)}")
        return jsonify(create_response(False, f'获取脚本内容失败: {str(e)}')), 500


@api_bp.route('/scripts/<int:script_id>/content', methods=['PUT'])
@login_required
@log_api_exceptions
@log_api_call
def update_script_content(script_id):
    """更新脚本内容"""
    data = request.get_json()
    
    if not data or 'content' not in data:
        return jsonify(create_response(False, '请提供脚本内容')), 400
    
    script = UserScript.query.filter_by(id=script_id, user_id=current_user.id).first_or_404()
    
    try:
        # 获取文件夹路径（如果脚本在文件夹中）
        folder_path = None
        if script.folder_id:
            from services.shared.folder_utils import get_folder_full_path
            folder = ScriptFolder.query.filter_by(id=script.folder_id, user_id=current_user.id).first()
            if folder:
                relative_path, folder_path = get_folder_full_path(folder, current_user.id, current_user.username, return_both=True)
        
        # 获取脚本文件路径
        from services.shared.file_utils import get_user_script_path
        script_path = get_user_script_path(current_user.username, script.name, folder_path)
        
        # 确保目录存在
        os.makedirs(os.path.dirname(script_path), exist_ok=True)
        
        # 写入文件内容
        with open(script_path, 'w', encoding='utf-8') as f:
            f.write(data['content'])
        
        # 更新数据库中的更新时间
        script.updated_at = timezone_now()
        db.session.commit()
        
        # 记录操作
        log_user_action('INFO', 'script_api', f'用户 {current_user.username} 更新了脚本内容: {script.name}')
        log_file_operation(current_user.id, 'update', script_path)
        
        return jsonify(create_response(True, '脚本内容更新成功'))
        
    except Exception as e:
        logger.error(f"更新脚本内容失败: {str(e)}")
        return jsonify(create_response(False, f'更新脚本内容失败: {str(e)}')), 500


@api_bp.route('/scripts/<int:script_id>/test', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def execute_script_test(script_id):
    """执行脚本测试（通过Celery任务在executor容器中执行）"""
    try:
        user_id = current_user.id
        username = current_user.username
        
        # 获取脚本信息
        script = UserScript.query.filter_by(id=script_id, user_id=user_id).first()
        if not script:
            return create_response(False, '脚本不存在或无权限访问', status_code=404)
        
        # 获取文件夹路径（如果脚本在文件夹中）
        folder_path = None
        if script.folder_id:
            from services.shared.folder_utils import get_folder_full_path
            from services.shared.db_pool import get_db_session
            
            folder = ScriptFolder.query.filter_by(id=script.folder_id, user_id=user_id).first()
            if folder:
                # 使用当前数据库会话
                db_session = get_db_session()
                relative_path, folder_path = get_folder_full_path(folder, user_id, username, db_session=db_session, return_both=True)
        
        # 获取脚本文件路径
        from services.shared.file_utils import get_user_script_path
        script_path = get_user_script_path(username, script.name, folder_path)
        
        if not os.path.exists(script_path):
            return create_response(False, '脚本文件不存在', status_code=404)
        
        # 读取脚本内容
        with open(script_path, 'r', encoding='utf-8') as f:
            script_content = f.read()
        
        # 生成测试执行ID
        test_execution_id = f"test_{script_id}_{int(time.time())}"
        
        # 记录操作
        log_user_action('INFO', 'script_api', f'用户 {username} 执行脚本测试: {script.name}', user_id=user_id)
        
        # 通过Celery任务在executor容器中执行脚本测试
        from services.shared.celery_app import celery_app
        task = celery_app.send_task(
            'services.executor.tasks.execute_script_test',
            args=[
                test_execution_id,
                script.name,
                script_content,
                script.language or 'python',
                username,
                user_id
            ]
        )
        
        return create_response(True, '脚本测试已启动', {
            'test_execution_id': test_execution_id,
            'task_id': task.id,
            'script_name': script.name,
            'language': script.language or 'python'
        })
        
    except Exception as e:
        logger.error(f"启动脚本测试失败: {str(e)}")
        return create_response(False, f'启动脚本测试失败: {str(e)}', status_code=500)


@api_bp.route('/scripts/<int:script_id>/stop', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def stop_script_execution(script_id):
    """停止脚本执行"""
    script = UserScript.query.filter_by(id=script_id, user_id=current_user.id).first_or_404()
    
    try:
        # 这里可以实现停止脚本执行的逻辑
        # 由于Server-Sent Events的特性，客户端断开连接时进程会自动终止
        log_user_action('INFO', 'script_api', f'用户 {current_user.username} 停止脚本执行: {script.name}')
        
        return jsonify(create_response(True, '脚本执行已停止'))
        
    except Exception as e:
        logger.error(f"停止脚本执行失败: {str(e)}")
        return jsonify(create_response(False, f'停止脚本执行失败: {str(e)}')), 500


@api_bp.route('/scripts/batch-delete', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def batch_delete_scripts():
    """批量删除脚本（软删除）"""
    data = request.get_json()
    
    if not data or 'script_ids' not in data:
        return jsonify(create_response(False, '请提供要删除的脚本ID列表')), 400
    
    script_ids = data['script_ids']
    
    if not isinstance(script_ids, list) or not script_ids:
        return jsonify(create_response(False, '脚本ID列表不能为空')), 400
    
    try:
        # 查询所有要删除的脚本，确保都属于当前用户
        scripts = UserScript.query.filter(
            UserScript.id.in_(script_ids),
            UserScript.user_id == current_user.id
        ).all()
        
        if len(scripts) != len(script_ids):
            return jsonify(create_response(False, '部分脚本不存在或无权限删除')), 403
        
        deleted_scripts = []
        failed_scripts = []
        
        # 软删除：移动到回收站
        from services.shared.recycle_service import RecycleService
        
        for script in scripts:
            try:
                script_name = script.name
                script_id = script.id
                
                # 使用软删除服务
                RecycleService.soft_delete_script(script, current_user.id, current_user.username)
                
                deleted_scripts.append({'id': script_id, 'name': script_name})
                
            except Exception as e:
                logger.error(f"删除脚本 {script.name} 失败: {str(e)}")
                failed_scripts.append({'id': script.id, 'name': script.name, 'error': str(e)})
        
        # 记录系统日志
        log = SystemLog(
            level='INFO',
            module='script_api',
            message=f'用户 {current_user.username} 批量删除了 {len(deleted_scripts)} 个脚本到回收站',
            user_id=current_user.id,
            details=str({
                'deleted_scripts': [s['name'] for s in deleted_scripts],
                'failed_scripts': [s['name'] for s in failed_scripts]
            })
        )
        db.session.add(log)
        db.session.commit()
        
        result_data = {
            'deleted_count': len(deleted_scripts),
            'failed_count': len(failed_scripts),
            'deleted_scripts': deleted_scripts,
            'failed_scripts': failed_scripts
        }
        
        if failed_scripts:
            return jsonify(create_response(True, f'批量删除完成，成功删除 {len(deleted_scripts)} 个脚本到回收站，{len(failed_scripts)} 个失败', result_data))
        else:
            return jsonify(create_response(True, f'成功删除 {len(deleted_scripts)} 个脚本', result_data))
        
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'批量删除脚本失败: {str(e)}')), 500


@api_bp.route('/script-folders/batch-delete', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def batch_delete_folders():
    """批量删除文件夹（软删除）"""
    data = request.get_json()
    
    if not data or 'folder_ids' not in data:
        return jsonify(create_response(False, '请提供要删除的文件夹ID列表')), 400
    
    folder_ids = data['folder_ids']
    force_delete = data.get('force_delete', False)  # 获取强制删除参数
    
    if not isinstance(folder_ids, list) or not folder_ids:
        return jsonify(create_response(False, '文件夹ID列表不能为空')), 400
    
    try:
        # 查询所有要删除的文件夹，确保都属于当前用户
        folders = ScriptFolder.query.filter(
            ScriptFolder.id.in_(folder_ids),
            ScriptFolder.user_id == current_user.id
        ).all()
        
        if len(folders) != len(folder_ids):
            return jsonify(create_response(False, '部分文件夹不存在或无权限删除')), 403
        
        deleted_folders = []
        failed_folders = []
        
        from services.shared.recycle_service import RecycleService
        
        for folder in folders:
            try:
                folder_name = folder.name
                folder_id = folder.id
                
                # 使用软删除服务，传递force_delete参数
                success = RecycleService.soft_delete_folder(folder, current_user.id, current_user.username, force=force_delete)
                
                if success:
                    deleted_folders.append({'id': folder_id, 'name': folder_name})
                else:
                    # 删除失败，通常是因为文件夹不为空
                    error_msg = "文件夹不为空，无法删除" if not force_delete else "删除失败"
                    logger.warning(f"删除文件夹 {folder_name} 失败: {error_msg}")
                    failed_folders.append({'id': folder_id, 'name': folder_name, 'error': error_msg})
                
            except Exception as e:
                logger.error(f"删除文件夹 {folder.name} 失败: {str(e)}")
                failed_folders.append({'id': folder.id, 'name': folder.name, 'error': str(e)})
        
        # 提交数据库更改
        db.session.commit()
        
        # 记录用户操作日志
        delete_type = "强制删除" if force_delete else "删除"
        log_user_action(
            user_id=current_user.id,
            level='INFO',
            module='web.api',
            action='batch_delete_folders' if not force_delete else 'batch_force_delete_folders',
            resource_type='script_folder',
            resource_id=None,
            message=f'用户 {current_user.username} 批量{delete_type}了 {len(deleted_folders)} 个文件夹到回收站',
            details=str({
                'deleted_folders': [f['name'] for f in deleted_folders],
                'failed_folders': [f['name'] for f in failed_folders],
                'force_delete': force_delete
            })
        )
        
        result_data = {
            'deleted_count': len(deleted_folders),
            'failed_count': len(failed_folders),
            'deleted_folders': deleted_folders,
            'failed_folders': failed_folders
        }
        
        if failed_folders:
            return jsonify(create_response(True, f'批量{delete_type}完成，成功移动 {len(deleted_folders)} 个文件夹到回收站，{len(failed_folders)} 个失败', result_data))
        else:
            return jsonify(create_response(True, f'成功移动 {len(deleted_folders)} 个文件夹到回收站', result_data))
        
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'批量删除文件夹失败: {str(e)}')), 500


@api_bp.route('/script-folders/<int:folder_id>/force-delete', methods=['DELETE'])
@login_required
@log_api_exceptions
@log_api_call
def force_delete_script_folder(folder_id):
    """强制删除脚本文件夹（包括非空文件夹）- 移动到回收站"""
    folder = ScriptFolder.query.filter_by(id=folder_id, user_id=current_user.id).first_or_404()
    
    try:
        folder_name = folder.name
        
        # 使用回收站服务进行软删除，即使是强制删除也保存到回收站
        from services.shared.recycle_service import RecycleService
        RecycleService.soft_delete_folder(folder, current_user.id, current_user.username, force=True)
        
        # 记录用户操作日志
        log_user_action(
            user_id=current_user.id,
            level='INFO',
            module='web.api',
            action='force_delete_folder',
            resource_type='script_folder',
            resource_id=folder_id,
            message=f'用户 {current_user.username} 强制删除了文件夹: {folder_name}（已移动到回收站）'
        )
        
        return jsonify(create_response(True, '文件夹已移动到回收站'))
        
    except Exception as e:
        db.session.rollback()
        logger.error(f"强制删除文件夹失败: {str(e)}")
        return jsonify(create_response(False, f'强制删除文件夹失败: {str(e)}')), 500


@api_bp.route('/system/config', methods=['GET'])
@login_required
@admin_required
@log_api_exceptions
@log_api_call
def get_system_config():
    """获取系统配置"""
    try:
        from ..shared.models.system import SystemConfig
        
        # 从数据库获取系统配置，如果不存在则使用默认值
        config = {
            'log_retention_days': SystemConfig.get_config('log_retention_days', 30),
            'execution_retention_days': SystemConfig.get_config('execution_retention_days', 90)
        }
        
        return jsonify(create_response(True, '获取系统配置成功', config))
        
    except Exception as e:
        return jsonify(create_response(False, f'获取系统配置失败: {str(e)}')), 500


@api_bp.route('/system/config', methods=['POST'])
@login_required
@admin_required
@log_api_exceptions
@log_api_call
def save_system_config():
    """保存系统配置"""
    try:
        from ..shared.models.system import SystemConfig
        
        data = request.get_json()
        
        # 验证配置数据
        if not data:
            return jsonify(create_response(False, '配置数据不能为空')), 400
            
        # 验证必需字段
        required_fields = ['log_retention_days', 'execution_retention_days']
        for field in required_fields:
            if field not in data:
                return jsonify(create_response(False, f'缺少必需字段: {field}')), 400
                
        # 验证数值范围
            
        if data['log_retention_days'] < 1 or data['log_retention_days'] > 365:
            return jsonify(create_response(False, '日志保留天数必须在1-365之间')), 400
            
        if data['execution_retention_days'] < 1 or data['execution_retention_days'] > 365:
            return jsonify(create_response(False, '执行记录保留天数必须在1-365之间')), 400
        
        # 保存配置到数据库
        SystemConfig.set_config('log_retention_days', data['log_retention_days'], 'int', 
                               '日志保留天数', current_user.id)
        SystemConfig.set_config('execution_retention_days', data['execution_retention_days'], 'int', 
                               '执行日志保留天数', current_user.id)
        
        # 记录系统日志
        log = SystemLog(
            level='INFO',
            module='system_config',
            message=f'用户 {current_user.username} 更新了系统配置',
            user_id=current_user.id,
            details=str({
                'config_changes': data
            })
        )
        db.session.add(log)
        db.session.commit()
        
        return jsonify(create_response(True, '系统配置保存成功'))
        
    except Exception as e:
        db.session.rollback()
        return jsonify(create_response(False, f'保存系统配置失败: {str(e)}')), 500


@api_bp.route('/executions/<execution_id>/stop', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def stop_execution(execution_id):
    """停止正在运行的任务执行"""
    logger.debug(f"开始处理停止任务执行请求，执行ID: {execution_id}，用户ID: {current_user.id}")
    
    try:
        # 查找执行记录
        execution = TaskExecution.query.filter_by(execution_id=execution_id).first()
        if not execution:
            logger.debug(f"未找到执行记录: {execution_id}")
            return jsonify(create_response(False, '执行记录不存在')), 404
        
        # 检查权限 - 只能停止自己的任务
        task = Task.query.get(execution.task_id)
        if not task or task.user_id != current_user.id:
            logger.debug(f"权限检查失败: 用户ID {current_user.id} 尝试停止其他用户的任务")
            return jsonify(create_response(False, '权限不足')), 403
        
        # 检查任务状态
        if execution.status not in [TaskStatus.PENDING, TaskStatus.RUNNING]:
            logger.debug(f"任务状态不允许停止: {execution.status.value}")
            return jsonify(create_response(False, f'任务状态为{execution.status.value}，无法停止')), 400
        
        # 调用执行器停止任务
        logger.debug(f"调用执行器停止任务: {execution_id}")
        executor_client = get_executor_client()
        result = executor_client.stop_task(execution_id)
        
        if result.get('success'):
            # 更新执行记录状态
            execution.status = TaskStatus.CANCELLED
            execution.completed_at = datetime.utcnow()
            execution.error_message = '任务被用户手动停止'
            db.session.commit()
            
            # 记录操作日志
            log_task_operation(
                action='stop',
                task_id=execution.task_id,
                task_name=task.name,
                user_id=current_user.id,
                execution_time=None,
                status_code=200,
                error_message=None,
                details=f'停止任务执行: {execution_id}'
            )
            
            logger.debug(f"任务停止成功: {execution_id}")
            return jsonify(create_response(True, '任务已停止'))
        else:
            error_msg = result.get('error', '未知错误')
            # 如果错误信息包含CANCELLED，这实际上表示任务已被成功停止
            if 'CANCELLED' in error_msg.upper():
                # 更新执行记录状态
                execution.status = TaskStatus.CANCELLED
                execution.completed_at = datetime.utcnow()
                execution.error_message = '任务被用户手动停止'
                db.session.commit()
                
                # 记录操作日志
                log_task_operation(
                    action='stop',
                    task_id=execution.task_id,
                    task_name=task.name,
                    user_id=current_user.id,
                    execution_time=None,
                    status_code=200,
                    error_message=None,
                    details=f'停止任务执行: {execution_id}'
                )
                
                logger.debug(f"任务停止成功(CANCELLED): {execution_id}")
                return jsonify(create_response(True, '任务已停止'))
            else:
                logger.error(f"执行器停止任务失败: {error_msg}")
                return jsonify(create_response(False, f'停止任务失败: {error_msg}')), 500
            
    except Exception as e:
        # 如果异常是CANCELLED，这实际上表示任务已被成功停止
        if 'CANCELLED' in str(e).upper():
            try:
                # 重新查找执行记录
                execution = TaskExecution.query.filter_by(execution_id=execution_id).first()
                if execution:
                    task = Task.query.get(execution.task_id)
                    # 更新执行记录状态
                    execution.status = TaskStatus.CANCELLED
                    execution.completed_at = datetime.utcnow()
                    execution.error_message = '任务被用户手动停止'
                    db.session.commit()
                    
                    # 记录操作日志
                    if task:
                        log_task_operation(
                            action='stop',
                            task_id=execution.task_id,
                            task_name=task.name,
                            user_id=current_user.id,
                            execution_time=None,
                            status_code=200,
                            error_message=None,
                            details=f'停止任务执行: {execution_id}'
                        )
                
                logger.debug(f"任务停止成功(异常CANCELLED): {execution_id}")
                return jsonify(create_response(True, '任务已停止'))
            except Exception as db_e:
                logger.error(f"处理CANCELLED异常时数据库操作失败: {str(db_e)}")
                db.session.rollback()
                return jsonify(create_response(True, '任务已停止'))  # 即使数据库更新失败，任务实际已停止
        else:
            logger.error(f"停止任务执行时发生异常: {str(e)}")
            db.session.rollback()
            return jsonify(create_response(False, f'停止任务失败: {str(e)}')), 500


@api_bp.route('/websocket/emit', methods=['POST'])
@log_api_call
def emit_websocket_message():
    """接收来自Celery worker的WebSocket消息并转发"""
    try:
        data = request.get_json()
        logger.info(f'收到WebSocket emit请求: {data}')
        
        if not data:
            return jsonify({'success': False, 'message': '无效的请求数据'}), 400
        
        event_type = data.get('event_type')
        event_data = data.get('event_data', {})
        room = data.get('room')
        
        logger.info(f'WebSocket事件详情 - 类型: {event_type}, 房间: {room}, 数据: {event_data}')
        
        if not event_type or not room:
            return jsonify({'success': False, 'message': '缺少必要参数'}), 400
        
        # 获取SocketIO实例并发送消息
        from flask import current_app
        socketio = current_app.extensions.get('socketio')
        logger.info(f'SocketIO实例状态: {socketio is not None}')
        
        if socketio:
            # 直接发送消息到房间，不依赖客户端检测
            # 因为get_participants在某些情况下可能不准确
            logger.info(f'准备发送WebSocket消息: {event_type} 到房间 {room}')
            
            # 尝试检查房间状态（仅用于日志记录）
            try:
                room_clients = socketio.server.manager.get_participants('/', room)
                client_list = list(room_clients) if room_clients else []
                logger.info(f'房间 {room} 中检测到的客户端数量: {len(client_list)}')
                if client_list:
                    logger.info(f'房间 {room} 中的客户端ID: {client_list}')
            except Exception as e:
                logger.warning(f'检查房间客户端时出错: {e}')
            
            # 无论如何都发送消息
            socketio.emit(event_type, event_data, room=room)
            logger.info(f'✅ 成功转发WebSocket消息: {event_type} 到房间 {room}')
            return jsonify({'success': True, 'message': '消息发送成功'})
        else:
            logger.warning('SocketIO实例不可用')
            return jsonify({'success': False, 'message': 'WebSocket服务不可用'}), 503
            
    except Exception as e:
        logger.error(f'转发WebSocket消息失败: {str(e)}', exc_info=True)
        return jsonify({'success': False, 'message': f'发送失败: {str(e)}'}), 500


# 回收站管理接口
@api_bp.route('/recycle', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_recycle_items():
    """获取回收站项目列表"""
    try:
        from services.shared.recycle_service import RecycleService
        
        page = request.args.get('page', 1, type=int)
        per_page = request.args.get('per_page', 20, type=int)
        item_type = request.args.get('type')  # 'script' 或 'folder'
        
        items = RecycleService.get_recycle_items(
            user_id=current_user.id,
            page=page,
            per_page=per_page,
            item_type=item_type
        )
        
        return jsonify(create_response(True, '获取成功', items))
        
    except Exception as e:
        logger.error(f'获取回收站项目失败: {str(e)}', exc_info=True)
        return jsonify(create_response(False, f'获取回收站项目失败: {str(e)}')), 500


@api_bp.route('/recycle/<int:item_id>/restore', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def restore_recycle_item(item_id):
    """恢复回收站项目"""
    try:
        from services.shared.recycle_service import RecycleService
        from services.shared.models.recycle import RecycleBin
        
        # 获取回收站项目
        item = RecycleBin.query.filter_by(id=item_id, user_id=current_user.id).first()
        if not item:
            return jsonify(create_response(False, '回收站项目不存在')), 404
        
        # 根据类型恢复
        if item.item_type == 'script':
            success, message = RecycleService.restore_script(item_id, current_user.id, current_user.username)
        elif item.item_type == 'folder':
            success, message = RecycleService.restore_folder(item_id, current_user.id, current_user.username)
        else:
            return jsonify(create_response(False, '不支持的项目类型')), 400
        
        if success:
            # 记录用户操作日志
            log_user_action(
                'INFO',
                'web.api',
                f'用户 {current_user.username} 恢复了{item.item_type}: {item.original_name}',
                user_id=current_user.id,
                action='restore',
                resource_type=item.item_type,
                resource_id=item_id
            )
            return jsonify(create_response(True, message))
        else:
            return jsonify(create_response(False, message)), 500
            
    except Exception as e:
        logger.error(f'恢复回收站项目失败: {str(e)}', exc_info=True)
        return jsonify(create_response(False, f'恢复失败: {str(e)}')), 500


@api_bp.route('/recycle/restore/<int:item_id>', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def restore_recycle_item_simple(item_id):
    """恢复回收站项目 - 简化路由"""
    return restore_recycle_item(item_id)


@api_bp.route('/recycle/<int:item_id>/delete', methods=['DELETE'])
@login_required
@log_api_exceptions
@log_api_call
def permanently_delete_recycle_item(item_id):
    """永久删除回收站项目"""
    try:
        from services.shared.recycle_service import RecycleService
        from services.shared.models.recycle import RecycleBin
        
        # 获取回收站项目
        item = RecycleBin.query.filter_by(id=item_id, user_id=current_user.id).first()
        if not item:
            return jsonify(create_response(False, '回收站项目不存在')), 404
        
        # 永久删除
        success = RecycleService.permanently_delete(item_id, current_user.id)
        
        if success:
            # 记录系统日志
            log_system_event(
                level='INFO',
                module='recycle',
                message=f'用户 {current_user.username} 永久删除了{item.item_type}: {item.original_name}',
                action='permanent_delete',
                resource_type=item.item_type,
                resource_id=str(item_id)
            )
            return jsonify(create_response(True, f'{item.item_type}永久删除成功'))
        else:
            return jsonify(create_response(False, f'{item.item_type}永久删除失败')), 500
            
    except Exception as e:
        logger.error(f'永久删除回收站项目失败: {str(e)}', exc_info=True)
        return jsonify(create_response(False, f'永久删除失败: {str(e)}')), 500


@api_bp.route('/recycle/batch-restore', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def batch_restore_recycle_items():
    """批量恢复回收站项目"""
    try:
        from services.shared.recycle_service import RecycleService
        from services.shared.models.recycle import RecycleBin
        
        data = request.get_json()
        item_ids = data.get('item_ids', [])
        
        if not item_ids:
            return jsonify(create_response(False, '请选择要恢复的项目')), 400
        
        success_count = 0
        failed_items = []
        
        for item_id in item_ids:
            try:
                # 获取回收站项目
                item = RecycleBin.query.filter_by(id=item_id, user_id=current_user.id).first()
                if not item:
                    failed_items.append(f'项目ID {item_id} 不存在')
                    continue
                
                # 根据类型恢复
                if item.item_type == 'script':
                    success, message = RecycleService.restore_script(item_id, current_user.id, current_user.username)
                elif item.item_type == 'folder':
                    success, message = RecycleService.restore_folder(item_id, current_user.id, current_user.username)
                else:
                    failed_items.append(f'{item.original_name}: 不支持的项目类型')
                    continue
                
                if success:
                    success_count += 1
                    # 记录用户操作日志
                    log_user_action(
                        'INFO',
                        'web.api',
                        f'用户 {current_user.username} 批量恢复了{item.item_type}: {item.original_name}',
                        user_id=current_user.id,
                        action='batch_restore',
                        resource_type=item.item_type,
                        resource_id=item_id
                    )
                else:
                    failed_items.append(f'{item.original_name}: {message}')
                    
            except Exception as e:
                failed_items.append(f'项目ID {item_id}: {str(e)}')
        
        # 构建响应消息
        if success_count == len(item_ids):
            message = f'成功恢复 {success_count} 个项目'
        elif success_count > 0:
            message = f'成功恢复 {success_count} 个项目，{len(failed_items)} 个失败'
        else:
            message = '所有项目恢复失败'
        
        return jsonify(create_response(
            success_count > 0,
            message,
            {
                'success_count': success_count,
                'failed_count': len(failed_items),
                'failed_items': failed_items
            }
        ))
            
    except Exception as e:
        logger.error(f'批量恢复回收站项目失败: {str(e)}', exc_info=True)
        return jsonify(create_response(False, f'批量恢复失败: {str(e)}')), 500


@api_bp.route('/recycle/batch-restore-simple', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def batch_restore_recycle_items_simple():
    """批量恢复回收站项目 - 简化路由"""
    return batch_restore_recycle_items()


@api_bp.route('/recycle/items/batch-delete', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def batch_permanently_delete_recycle_items():
    """批量永久删除回收站项目"""
    try:
        from services.shared.recycle_service import RecycleService
        from services.shared.models.recycle import RecycleBin
        
        data = request.get_json()
        item_ids = data.get('item_ids', [])
        
        if not item_ids:
            return jsonify(create_response(False, '请选择要删除的项目')), 400
        
        success_count = 0
        failed_items = []
        
        for item_id in item_ids:
            try:
                # 获取回收站项目
                item = RecycleBin.query.filter_by(id=item_id, user_id=current_user.id).first()
                if not item:
                    failed_items.append(f'项目ID {item_id} 不存在')
                    continue
                
                # 永久删除
                success = RecycleService.permanently_delete(item_id, current_user.id)
                
                if success:
                    success_count += 1
                    # 记录系统日志
                    log_system_event(
                        level='INFO',
                        module='recycle',
                        message=f'用户 {current_user.username} 批量永久删除了{item.item_type}: {item.original_name}',
                        action='batch_permanent_delete',
                        resource_type='recycle_item',
                        resource_id=item_id
                    )
                else:
                    failed_items.append(f'{item.original_name}: 删除失败')
                    
            except Exception as e:
                failed_items.append(f'项目ID {item_id}: {str(e)}')
        
        # 构建响应消息
        if success_count == len(item_ids):
            message = f'成功删除 {success_count} 个项目'
        elif success_count > 0:
            message = f'成功删除 {success_count} 个项目，{len(failed_items)} 个失败'
        else:
            message = '所有项目删除失败'
        
        return jsonify(create_response(
            success_count > 0,
            message,
            {
                'success_count': success_count,
                'failed_count': len(failed_items),
                'failed_items': failed_items
            }
        ))
        
    except Exception as e:
        logger.error(f'批量永久删除回收站项目失败: {str(e)}', exc_info=True)
        return jsonify(create_response(False, f'批量永久删除失败: {str(e)}')), 500


@api_bp.route('/recycle/clean', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def clean_expired_recycle_items():
    """清理过期的回收站项目"""
    try:
        from services.shared.recycle_service import RecycleService
        
        # 只有管理员可以执行清理操作
        if not current_user.is_admin:
            return jsonify(create_response(False, '权限不足')), 403
        
        cleaned_count = RecycleService.clean_expired_items()
        
        # 记录系统日志
        log_system_event(
            level='INFO',
            module='recycle',
            message=f'管理员 {current_user.username} 清理了 {cleaned_count} 个过期的回收站项目',
            action='clean_expired',
            resource_type='recycle_items',
            details=f'清理数量: {cleaned_count}'
        )
        
        return jsonify(create_response(
            True, 
            f'成功清理 {cleaned_count} 个过期项目',
            data={'cleaned_count': cleaned_count}
        ))
        
    except Exception as e:
        logger.error(f'清理过期回收站项目失败: {str(e)}', exc_info=True)
        return jsonify(create_response(False, f'清理失败: {str(e)}')), 500


@api_bp.route('/recycle/permanent-delete/<int:item_id>', methods=['DELETE'])
@login_required
@log_api_exceptions
@log_api_call
def permanent_delete_recycle_item_alt(item_id):
    """永久删除回收站项目 - 替代路由"""
    return permanently_delete_recycle_item(item_id)


@api_bp.route('/recycle/clean-expired', methods=['DELETE'])
@login_required
@log_api_exceptions
@log_api_call
def clean_expired_recycle_items_alt():
    """清理过期的回收站项目 - 替代路由"""
    return clean_expired_recycle_items()


@api_bp.route('/recycle/batch-permanent-delete', methods=['DELETE'])
@login_required
@log_api_exceptions
@log_api_call
def batch_permanent_delete_recycle_items_alt():
    """批量永久删除回收站项目（备用路由）"""
    return batch_permanently_delete_recycle_items()


# ==================== 公共参数管理接口 ====================

@api_bp.route('/global-parameters', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_global_parameters():
    """获取当前用户的公共参数列表"""
    try:
        parameters = GlobalParameter.get_user_parameters(current_user.id)
        
        # 转换为字典格式，隐藏加密参数的真实值
        parameter_list = []
        for param in parameters:
            param_dict = param.to_dict()
            if param.is_encrypted:
                param_dict['value'] = '***'  # 隐藏加密值
            parameter_list.append(param_dict)
        
        log_user_action(
            level='INFO',
            module='api',
            message='查看公共参数列表',
            user_id=current_user.id,
            action='VIEW_GLOBAL_PARAMETERS',
            details=f'获取到 {len(parameter_list)} 个参数'
        )
        
        return jsonify(create_response(True, '获取成功', parameter_list))
        
    except Exception as e:
        logger.error(f"获取公共参数列表失败: {e}")
        return jsonify(create_response(False, f'获取失败: {str(e)}')), 500


@api_bp.route('/global-parameters', methods=['POST'])
@login_required
@log_api_exceptions
@log_api_call
def create_global_parameter():
    """创建新的公共参数"""
    try:
        data = request.get_json()
        
        # 验证必需字段
        if not data or not data.get('name') or not data.get('value'):
            return jsonify(create_response(False, '参数名称和值不能为空')), 400
        
        # 验证参数名称格式
        import re
        if not re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', data['name']):
            return jsonify(create_response(False, '参数名称格式不正确，只能包含字母、数字和下划线，且必须以字母或下划线开头')), 400
        
        # 检查参数名称是否已存在
        existing_param = GlobalParameter.query.filter_by(
            user_id=current_user.id,
            name=data['name']
        ).first()
        
        if existing_param:
            return jsonify(create_response(False, '参数名称已存在')), 400
        
        # 处理参数值（如果是加密参数则进行加密）
        param_value = data['value']
        is_encrypted = data.get('is_encrypted', False)
        
        if is_encrypted:
            from services.shared.crypto_utils import encrypt_value
            try:
                param_value = encrypt_value(param_value)
            except Exception as encrypt_error:
                logger.error(f"加密参数值失败: {encrypt_error}")
                return jsonify(create_response(False, f'参数加密失败: {str(encrypt_error)}')), 500
        
        # 创建新参数
        parameter = GlobalParameter(
            name=data['name'],
            value=param_value,
            description=data.get('description', ''),
            is_encrypted=is_encrypted,
            is_enabled=data.get('is_enabled', True),
            user_id=current_user.id
        )
        
        db.session.add(parameter)
        db.session.commit()
        
        log_user_action(
            level='INFO',
            module='api',
            message='创建公共参数',
            user_id=current_user.id,
            action='CREATE_GLOBAL_PARAMETER',
            details=f'参数名: {parameter.name}, 加密: {parameter.is_encrypted}'
        )
        
        return jsonify(create_response(True, '参数创建成功', parameter.to_dict()))
        
    except Exception as e:
        db.session.rollback()
        logger.error(f"创建公共参数失败: {e}")
        return jsonify(create_response(False, f'创建失败: {str(e)}')), 500


@api_bp.route('/global-parameters/<int:param_id>', methods=['GET'])
@login_required
@log_api_exceptions
@log_api_call
def get_global_parameter(param_id):
    """获取指定的公共参数详情"""
    try:
        parameter = GlobalParameter.query.filter_by(
            id=param_id,
            user_id=current_user.id
        ).first()
        
        if not parameter:
            return jsonify(create_response(False, '参数不存在')), 404
        
        # 返回完整信息（包括加密参数的真实值，用于编辑）
        param_dict = parameter.to_dict_with_value()
        
        return jsonify(create_response(True, '获取成功', param_dict))
        
    except Exception as e:
        logger.error(f"获取公共参数详情失败: {e}")
        return jsonify(create_response(False, f'获取失败: {str(e)}')), 500


@api_bp.route('/global-parameters/<int:param_id>', methods=['PUT'])
@login_required
@log_api_exceptions
@log_api_call
def update_global_parameter(param_id):
    """更新公共参数"""
    try:
        parameter = GlobalParameter.query.filter_by(
            id=param_id,
            user_id=current_user.id
        ).first()
        
        if not parameter:
            return jsonify(create_response(False, '参数不存在')), 404
        
        data = request.get_json()
        
        # 验证必需字段
        if not data or not data.get('name'):
            return jsonify(create_response(False, '参数名称不能为空')), 400
        
        # 验证参数名称格式
        import re
        if not re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', data['name']):
            return jsonify(create_response(False, '参数名称格式不正确，只能包含字母、数字和下划线，且必须以字母或下划线开头')), 400
        
        # 如果修改了名称，检查新名称是否已存在
        if data['name'] != parameter.name:
            existing_param = GlobalParameter.query.filter_by(
                user_id=current_user.id,
                name=data['name']
            ).first()
            
            if existing_param:
                return jsonify(create_response(False, '参数名称已存在')), 400
        
        # 更新参数
        old_name = parameter.name
        old_is_encrypted = parameter.is_encrypted
        parameter.name = data['name']
        parameter.description = data.get('description', '')
        parameter.is_encrypted = data.get('is_encrypted', False)
        parameter.is_enabled = data.get('is_enabled', True)
        
        # 处理参数值更新
        new_value = data.get('value')
        if new_value:
            # 如果是加密参数，需要加密新值
            if parameter.is_encrypted:
                from services.shared.crypto_utils import encrypt_value
                try:
                    parameter.value = encrypt_value(new_value)
                except Exception as encrypt_error:
                    logger.error(f"加密参数值失败: {encrypt_error}")
                    return jsonify(create_response(False, f'参数加密失败: {str(encrypt_error)}')), 500
            else:
                parameter.value = new_value
        elif not parameter.is_encrypted:
            # 非加密参数必须提供值
            return jsonify(create_response(False, '参数值不能为空')), 400
        elif old_is_encrypted != parameter.is_encrypted:
            # 如果从非加密改为加密，需要加密现有值
            if parameter.is_encrypted and not old_is_encrypted:
                from services.shared.crypto_utils import encrypt_value
                try:
                    parameter.value = encrypt_value(parameter.value)
                except Exception as encrypt_error:
                    logger.error(f"加密现有参数值失败: {encrypt_error}")
                    return jsonify(create_response(False, f'参数加密失败: {str(encrypt_error)}')), 500
            # 如果从加密改为非加密，需要解密现有值
            elif not parameter.is_encrypted and old_is_encrypted:
                from services.shared.crypto_utils import decrypt_value
                try:
                    parameter.value = decrypt_value(parameter.value)
                except Exception as decrypt_error:
                    logger.error(f"解密现有参数值失败: {decrypt_error}")
                    return jsonify(create_response(False, f'参数解密失败: {str(decrypt_error)}')), 500
        
        db.session.commit()
        
        log_user_action(
            level='INFO',
            module='api',
            message='更新公共参数',
            user_id=current_user.id,
            action='UPDATE_GLOBAL_PARAMETER',
            details=f'参数名: {old_name} -> {parameter.name}, 加密: {parameter.is_encrypted}'
        )
        
        return jsonify(create_response(True, '参数更新成功', parameter.to_dict()))
        
    except Exception as e:
        db.session.rollback()
        logger.error(f"更新公共参数失败: {e}")
        return jsonify(create_response(False, f'更新失败: {str(e)}')), 500


@api_bp.route('/global-parameters/<int:param_id>', methods=['DELETE'])
@login_required
@log_api_exceptions
@log_api_call
def delete_global_parameter(param_id):
    """删除公共参数"""
    try:
        parameter = GlobalParameter.query.filter_by(
            id=param_id,
            user_id=current_user.id
        ).first()
        
        if not parameter:
            return jsonify(create_response(False, '参数不存在')), 404
        
        param_name = parameter.name
        
        db.session.delete(parameter)
        db.session.commit()
        
        log_user_action(
            level='INFO',
            module='api',
            message='删除公共参数',
            user_id=current_user.id,
            action='DELETE_GLOBAL_PARAMETER',
            details=f'参数名: {param_name}'
        )
        
        return jsonify(create_response(True, '参数删除成功'))
        
    except Exception as e:
        db.session.rollback()
        logger.error(f"删除公共参数失败: {e}")
        return jsonify(create_response(False, f'删除失败: {str(e)}')), 500